Completed
Push — master ( 798535...6fb253 )
by Iqbal
02:31
created

Repository::paginate()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 3
eloc 5
nc 2
nop 0
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
        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...terface::withPagination 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...
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function findOne(array $fields)
130
    {
131
        return $this->addCriteria($fields)->first();
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function find(array $fields)
138
    {
139
        return $this->addCriteria($fields)->get();
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function findAll()
146
    {
147
        return $this->get();
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function remove($id)
154
    {
155
        $this->storage->remove($id, $this->table);
156
    }
157
158
    /*
159
     * {@inheritdoc}
160
     */
161
    public function reset()
162
    {
163
        $this->finder = null;
164
        $this->pagination = null;
165
166
        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...
167
    }
168
169
    /**
170
     * Get finder that used for find data with complex criteria.
171
     *
172
     * @return FinderInterface
173
     */
174
    protected function finder()
175
    {
176
        if (null === $this->finder) {
177
            $this->finder = $this->storage->finder($this->table, $this->class);
178
        }
179
180
        return $this->finder;
181
    }
182
183
    /**
184
     * Add criteria.
185
     *
186
     * @param array $fields
187
     *
188
     * @return static
189
     */
190
    protected function addCriteria(array $fields)
191
    {
192
        foreach ($fields as $field => $value) {
193
            $this->finder()->where($this->finder()->expr()->equal($field, $value));
194
        }
195
196
        return $this;
197
    }
198
199
    /**
200
     * Get a collection.
201
     *
202
     * @return Collection
203
     */
204
    protected function get()
205
    {
206
        $results = $this->paginate()->finder()->get();
207
        if (null !== $this->pagination) {
208
            $results->setPagination($this->pagination);
209
        }
210
211
        $this->reset();
212
213
        return $results;
214
    }
215
216
    /**
217
     * Find first record.
218
     *
219
     * @return ReadModelInterface
220
     */
221
    protected function first()
222
    {
223
        $result = $this->finder()->limit(1)->first();
224
        $this->reset();
225
226
        return $result;
227
    }
228
229
    /**
230
     * Assert that read model instance should be same.
231
     *
232
     * @param ReadModelInterface $model
233
     */
234
    protected function assertSameInstance(ReadModelInterface $model)
235
    {
236
        if (ltrim($this->class, '\\') !== ltrim(get_class($model), '\\')) {
237
            throw new InvalidArgumentException(sprintf(
238
                'Repository "%s" only accept read model "%s", but got "%s".',
239
                get_called_class(),
240
                $this->class,
241
                get_class($model)
242
            ));
243
        }
244
    }
245
246
    /**
247
     * Build pagination.
248
     */
249
    private function paginate()
250
    {
251
        if (null !== $this->pagination && 0 !== ($total = $this->finder()->count())) {
252
            $this->pagination->calc($total);
253
            $this->finder()->limit($this->pagination->limit, $this->pagination->current - 1);
254
        }
255
256
        return $this;
257
    }
258
}
259