Completed
Push — master ( ef2a3c...3739ec )
by Julián
10:08
created

RepositoryTrait::setObjectFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
/*
4
 * doctrine-base-repositories (https://github.com/juliangut/doctrine-base-repositories).
5
 * Doctrine2 utility repositories.
6
 *
7
 * @license MIT
8
 * @link https://github.com/juliangut/doctrine-base-repositories
9
 * @author Julián Gutiérrez <[email protected]>
10
 */
11
12
declare(strict_types=1);
13
14
namespace Jgut\Doctrine\Repository;
15
16
use Doctrine\Common\Util\Inflector;
17
18
/**
19
 * Repository trait.
20
 */
21
trait RepositoryTrait
22
{
23
    /**
24
     * Auto flush changes.
25
     *
26
     * @var bool
27
     */
28
    protected $autoFlush = false;
29
30
    /**
31
     * New object factory.
32
     *
33
     * @var callable
34
     */
35
    protected $objectFactory;
36
37
    /**
38
     * Get automatic manager flushing.
39
     *
40
     * @return bool
41
     */
42
    public function isAutoFlush(): bool
43
    {
44
        return $this->autoFlush;
45
    }
46
47
    /**
48
     * Set automatic manager flushing.
49
     *
50
     * @param bool $autoFlush
51
     */
52
    public function setAutoFlush(bool $autoFlush = true)
53
    {
54
        $this->autoFlush = $autoFlush === true;
55
    }
56
57
    /**
58
     * Manager flush.
59
     */
60
    public function flush()
61
    {
62
        $this->getManager()->flush();
63
    }
64
65
    /**
66
     * Set object factory.
67
     *
68
     * @param callable $objectFactory
69
     */
70
    public function setObjectFactory(callable $objectFactory)
71
    {
72
        $this->objectFactory = $objectFactory;
73
    }
74
75
    /**
76
     * Find one object by a set of criteria or create a new one.
77
     *
78
     * @param array $criteria
79
     *
80
     * @throws \RuntimeException
81
     *
82
     * @return object
83
     */
84
    public function findOneByOrGetNew(array $criteria)
85
    {
86
        $object = $this->findOneBy($criteria);
0 ignored issues
show
Bug introduced by
The method findOneBy() does not exist on Jgut\Doctrine\Repository\RepositoryTrait. Did you maybe mean findOneByOrGetNew()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
87
88
        if ($object === null) {
89
            $object = $this->getNew();
90
        }
91
92
        return $object;
93
    }
94
95
    /**
96
     * Get a new managed object instance.
97
     *
98
     * @throws \RuntimeException
99
     *
100
     * @return object
101
     */
102
    public function getNew()
103
    {
104
        $className = $this->getClassName();
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
105
106
        if ($this->objectFactory === null) {
107
            return new $className();
108
        }
109
110
        $object = call_user_func($this->objectFactory);
111
112
        if (!is_object($object) || !is_a($object, $className)) {
113
            throw new \RuntimeException(
114
                sprintf(
115
                    'Object factory must return an instance of %s. "%s" returned',
116
                    $className,
117
                    is_object($object) ? get_class($object) : gettype($object)
118
                )
119
            );
120
        }
121
122
        return $object;
123
    }
124
125
    /**
126
     * Add objects.
127
     *
128
     * @param \stdClass|\stdClass[] $objects
129
     * @param bool                  $flush
130
     *
131
     * @throws \InvalidArgumentException
132
     */
133
    public function add($objects, bool $flush = false)
134
    {
135
        if (!is_array($objects)) {
136
            $objects = [$objects];
137
        }
138
139
        $manager = $this->getManager();
140
141
        foreach ($objects as $object) {
142
            if (!$this->canBeManaged($object)) {
143
                throw new \InvalidArgumentException(sprintf('Managed object must be a %s', $this->getClassName()));
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
144
            }
145
146
            $manager->persist($object);
147
        }
148
149
        if ($flush === true || $this->autoFlush === true) {
150
            $manager->flush();
151
        }
152
    }
153
154
    /**
155
     * Remove all objects.
156
     *
157
     * @param bool $flush
158
     */
159
    public function removeAll(bool $flush = false)
160
    {
161
        $manager = $this->getManager();
162
163
        foreach ($this->findAll() as $object) {
0 ignored issues
show
Documentation Bug introduced by
The method findAll does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
164
            $manager->remove($object);
165
        }
166
167
        if ($flush === true || $this->autoFlush === true) {
168
            $manager->flush();
169
        }
170
    }
171
172
    /**
173
     * Remove object filtered by a set of criteria.
174
     *
175
     * @param array $criteria
176
     * @param bool  $flush
177
     */
178
    public function removeBy(array $criteria, bool $flush = false)
179
    {
180
        $manager = $this->getManager();
181
182
        foreach ($this->findBy($criteria) as $object) {
0 ignored issues
show
Documentation Bug introduced by
The method findBy does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
183
            $manager->remove($object);
184
        }
185
186
        if ($flush === true || $this->autoFlush === true) {
187
            $manager->flush();
188
        }
189
    }
190
191
    /**
192
     * Remove first object filtered by a set of criteria.
193
     *
194
     * @param array $criteria
195
     * @param bool  $flush
196
     */
197
    public function removeOneBy(array $criteria, bool $flush = false)
198
    {
199
        $object = $this->findOneBy($criteria);
0 ignored issues
show
Bug introduced by
The method findOneBy() does not exist on Jgut\Doctrine\Repository\RepositoryTrait. Did you maybe mean findOneByOrGetNew()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
200
201
        if ($object !== null) {
202
            $manager = $this->getManager();
203
204
            $manager->remove($object);
205
206
            if ($flush === true || $this->autoFlush === true) {
207
                $manager->flush();
208
            }
209
        }
210
    }
211
212
    /**
213
     * Remove objects.
214
     *
215
     * @param object|object[]|string|int $objects
216
     * @param bool                       $flush
217
     *
218
     * @throws \InvalidArgumentException
219
     */
220
    public function remove($objects, bool $flush = false)
221
    {
222
        $manager = $this->getManager();
223
224
        if (!is_object($objects) && !is_array($objects)) {
225
            $objects = $this->find($objects);
0 ignored issues
show
Documentation Bug introduced by
The method find does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
226
        }
227
228
        if ($objects !== null) {
229
            if (!is_array($objects)) {
230
                $objects = [$objects];
231
            }
232
233
            foreach ($objects as $object) {
234
                if (!$this->canBeManaged($object)) {
235
                    throw new \InvalidArgumentException(sprintf('Managed object must be a %s', $this->getClassName()));
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
236
                }
237
238
                $manager->remove($object);
239
            }
240
241
            if ($flush === true || $this->autoFlush === true) {
242
                $manager->flush();
243
            }
244
        }
245
    }
246
247
    /**
248
     * Get all objects count.
249
     *
250
     * @return int
251
     */
252
    public function countAll(): int
253
    {
254
        return $this->countBy([]);
255
    }
256
257
    /**
258
     * Get object count filtered by a set of criteria.
259
     *
260
     * @param mixed $criteria
261
     *
262
     * @return int
263
     */
264
    abstract public function countBy($criteria): int;
265
266
    /**
267
     * Adds support for magic finders and removers.
268
     *
269
     * @param string $method
270
     * @param array  $arguments
271
     *
272
     * @throws \BadMethodCallException
273
     *
274
     * @return mixed
275
     */
276
    public function __call($method, $arguments)
277
    {
278
        static $supportedMethods = ['findBy', 'findOneBy', 'findPaginatedBy', 'removeBy', 'removeOneBy'];
279
280
        if (count($arguments) === 0) {
281
            throw new \BadMethodCallException(sprintf(
282
                'You need to pass a parameter to %s::%s',
283
                $this->getClassName(),
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
284
                $method
285
            ));
286
        }
287
288
        foreach ($supportedMethods as $supportedMethod) {
289
            if (strpos($method, $supportedMethod) === 0) {
290
                $field = substr($method, strlen($supportedMethod));
291
                $method = substr($method, 0, strlen($supportedMethod));
292
293
                return $this->callSupportedMethod($method, Inflector::camelize($field), $arguments);
294
            }
295
        }
296
297
        throw new \BadMethodCallException(sprintf(
298
            'Undefined method "%s". Method name must start with one of "%s"!',
299
            $method,
300
            implode('", "', $supportedMethods)
301
        ));
302
    }
303
304
    /**
305
     * Internal remove magic finder.
306
     *
307
     * @param string $method
308
     * @param string $fieldName
309
     * @param array  $arguments
310
     *
311
     * @throws \BadMethodCallException
312
     *
313
     * @return mixed
314
     */
315
    protected function callSupportedMethod(string $method, string $fieldName, array $arguments)
316
    {
317
        $classMetadata = $this->getClassMetadata();
318
319
        if ($classMetadata->hasField($fieldName) || $classMetadata->hasAssociation($fieldName)) {
320
            // @codeCoverageIgnoreStart
321
            $parameters = array_merge(
322
                [$fieldName => $arguments[0]],
323
                array_slice($arguments, 1)
324
            );
325
326
            return call_user_func_array([$this, $method], $parameters);
327
            // @codeCoverageIgnoreEnd
328
        }
329
330
        throw new \BadMethodCallException(sprintf(
331
            'Invalid call to %s::%s. Field "%s" does not exist',
332
            $this->getClassName(),
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
333
            $method,
334
            $fieldName
335
        ));
336
    }
337
338
    /**
339
     * Check if the object is of the proper type.
340
     *
341
     * @param object $object
342
     *
343
     * @return bool
344
     */
345
    protected function canBeManaged($object): bool
346
    {
347
        return is_object($object) && is_a($object, $this->getClassName());
0 ignored issues
show
Documentation Bug introduced by
The method getClassName does not exist on object<Jgut\Doctrine\Repository\RepositoryTrait>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
348
    }
349
350
    /**
351
     * Get object manager.
352
     *
353
     * @return \Doctrine\Common\Persistence\ObjectManager
354
     */
355
    abstract protected function getManager();
356
357
    /**
358
     * Get class metadata.
359
     *
360
     * @return \Doctrine\Common\Persistence\Mapping\ClassMetadata
361
     */
362
    abstract protected function getClassMetadata();
363
}
364