Completed
Push — master ( ea613e...54b4d6 )
by Iqbal
02:33
created

Repository::withPagination()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
/*
3
 * This file is part of the Borobudur-Cqrs package.
4
 *
5
 * (c) Hexacodelabs <http://hexacodelabs.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Borobudur\Cqrs\ReadModel\Repository;
12
13
use Borobudur\Bus\Message\MessageMapperTrait;
14
use Borobudur\Cqrs\Collection;
15
use Borobudur\Cqrs\Exception\InvalidArgumentException;
16
use Borobudur\Cqrs\ReadModel\ReadModelInterface;
17
use Borobudur\Cqrs\ReadModel\Repository\Specification\SpecificationInterface;
18
use Borobudur\Cqrs\ReadModel\Storage\Finder\FinderInterface;
19
use Borobudur\Cqrs\ReadModel\Storage\Finder\Pagination;
20
use Borobudur\Cqrs\ReadModel\Storage\StorageInterface;
21
22
/**
23
 * @author      Iqbal Maulana <[email protected]>
24
 * @created     8/19/15
25
 */
26
class Repository implements RepositoryInterface
27
{
28
    use MessageMapperTrait;
29
30
    /**
31
     * @var Pagination
32
     */
33
    protected $pagination;
34
35
    /**
36
     * @var StorageInterface
37
     */
38
    private $storage;
39
40
    /**
41
     * @var string
42
     */
43
    private $table;
44
45
    /**
46
     * @var string
47
     */
48
    private $class;
49
50
    /**
51
     * @var FinderInterface
52
     */
53
    private $finder;
54
55
    /**
56
     * Constructor.
57
     *
58
     * @param StorageInterface $storage
59
     * @param string           $table
60
     * @param string           $class
61
     */
62
    public function __construct(StorageInterface $storage, $table, $class)
63
    {
64
        $this->storage = $storage;
65
        $this->table = $table;
66
        $this->class = $class;
67
    }
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function save(ReadModelInterface $model)
73
    {
74
        $this->assertSameInstance($model);
75
        $this->storage->save($model, $this->table);
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function count()
82
    {
83
        return $this->finder()->count();
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function findById($id)
90
    {
91
        return $this->addCriteria(array('id' => $id))->first();
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function withSpecifications(array $specifications)
98
    {
99
        foreach ($specifications as $specification) {
100
            $this->withSpecification($specification);
101
        }
102
103
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Borobudur\Cqrs\ReadModel\Repository\Repository) is incompatible with the return type declared by the interface Borobudur\Cqrs\ReadModel...ace::withSpecifications of type Borobudur\Cqrs\ReadModel\ReadModelInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function withSpecification(SpecificationInterface $specification)
110
    {
111
        $specification->matching($this->finder());
112
113
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Borobudur\Cqrs\ReadModel\Repository\Repository) is incompatible with the return type declared by the interface Borobudur\Cqrs\ReadModel...face::withSpecification of type Borobudur\Cqrs\ReadModel\ReadModelInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119
    public function withPagination($limit, $page)
120
    {
121
        $this->pagination = new Pagination($limit, $page);
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function findOne(array $fields)
128
    {
129
        return $this->addCriteria($fields)->first();
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function find(array $fields)
136
    {
137
        return $this->addCriteria($fields)->get();
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function findAll()
144
    {
145
        return $this->get();
146
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151
    public function remove($id)
152
    {
153
        $this->storage->remove($id, $this->table);
154
    }
155
156
    /*
157
     * {@inheritdoc}
158
     */
159
    public function reset()
160
    {
161
        $this->finder = null;
162
        $this->pagination = null;
163
164
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Borobudur\Cqrs\ReadModel\Repository\Repository) is incompatible with the return type declared by the interface Borobudur\Cqrs\ReadModel...ositoryInterface::reset of type Borobudur\Cqrs\ReadModel\ReadModelInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
165
    }
166
167
    /**
168
     * Get finder that used for find data with complex criteria.
169
     *
170
     * @return FinderInterface
171
     */
172
    protected function finder()
173
    {
174
        if (null === $this->finder) {
175
            $this->finder = $this->storage->finder($this->table, $this->class);
176
        }
177
178
        return $this->finder;
179
    }
180
181
    /**
182
     * Add criteria.
183
     *
184
     * @param array $fields
185
     *
186
     * @return static
187
     */
188
    protected function addCriteria(array $fields)
189
    {
190
        foreach ($fields as $field => $value) {
191
            $this->finder()->where($this->finder()->expr()->equal($field, $value));
192
        }
193
194
        return $this;
195
    }
196
197
    /**
198
     * Get a collection.
199
     *
200
     * @return Collection
201
     */
202
    protected function get()
203
    {
204
        $results = $this->finder()->get();
205
        if (null !== $pagination = $this->pagination) {
206
            $results->setPagination($pagination);
207
        }
208
209
        $this->reset();
210
211
        return $results;
212
    }
213
214
    /**
215
     * Find first record.
216
     *
217
     * @return ReadModelInterface
218
     */
219
    protected function first()
220
    {
221
        $result = $this->finder()->limit(1)->first();
222
        $this->reset();
223
224
        return $result;
225
    }
226
227
    /**
228
     * Build pagination.
229
     */
230
    protected function buildPagination()
231
    {
232
        if (null !== $this->pagination && 0 !== ($total = $this->finder()->count())) {
233
            $this->pagination->calc($total);
234
235
            return $this->pagination;
236
        }
237
238
        return null;
239
    }
240
241
    /**
242
     * Assert that read model instance should be same.
243
     *
244
     * @param ReadModelInterface $model
245
     */
246
    protected function assertSameInstance(ReadModelInterface $model)
247
    {
248
        if (ltrim($this->class, '\\') !== ltrim(get_class($model), '\\')) {
249
            throw new InvalidArgumentException(sprintf(
250
                'Repository "%s" only accept read model "%s", but got "%s".',
251
                get_called_class(),
252
                $this->class,
253
                get_class($model)
254
            ));
255
        }
256
    }
257
}
258