Completed
Push — master ( 372519...d75aef )
by Mathieu
02:59
created

Content::modelFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Charcoal\Object;
4
5
use \DateTime;
6
use \InvalidArgumentException;
7
8
9
use \Pimple\Container;
10
11
use \Charcoal\Factory\FactoryInterface;
12
13
// From `charcoal-core`
14
use \Charcoal\Model\AbstractModel;
15
16
use \Charcoal\Object\ContentInterface;
17
use \Charcoal\Object\RevisionableInterface;
18
use \Charcoal\Object\RevisionableTrait;
19
20
/**
21
 *
22
 */
23
class Content extends AbstractModel implements
24
    ContentInterface,
25
    RevisionableInterface
26
{
27
    use RevisionableTrait;
28
29
    /**
30
     * Objects are active by default
31
     * @var boolean $Active
32
     */
33
    private $active = true;
34
35
    /**
36
     * The position is used for ordering lists
37
     * @var integer $Position
38
     */
39
    private $position = 0;
40
41
    /**
42
     * Object creation date (set automatically on save)
43
     * @var DateTime $Created
44
     */
45
    private $created;
46
47
    /**
48
     * @var mixed
49
     */
50
    private $createdBy;
51
52
    /**
53
     * Object last modified date (set automatically on save and update)
54
     * @var DateTime $LastModified
55
     */
56
    private $lastModified;
57
58
    /**
59
     * @var mixed
60
     */
61
    private $lastModifiedBy;
62
63
    /**
64
     * @var FactoryInterface $modelFactory
65
     */
66
    private $modelFactory;
67
68
    /**
69
     * Dependencies
70
     * @param Container $container DI Container.
71
     * @return void
72
     */
73
    public function setDependencies(Container $container)
74
    {
75
        parent::setDependencies($container);
76
77
        $this->setModelFactory($container['model/factory']);
78
    }
79
80
    /**
81
     * @param FactoryInterface $factory The factory used to create models.
82
     * @return AdminScript Chainable
83
     */
84
    protected function setModelFactory(FactoryInterface $factory)
85
    {
86
        $this->modelFactory = $factory;
87
        return $this;
88
    }
89
90
    /**
91
     * @return FactoryInterface The model factory.
92
     */
93
    protected function modelFactory()
94
    {
95
        return $this->modelFactory;
96
    }
97
98
    /**
99
     * @param boolean $active The active flag.
100
     * @return Content Chainable
101
     */
102
    public function setActive($active)
103
    {
104
        $this->active = !!$active;
105
        return $this;
106
    }
107
108
    /**
109
     * @return boolean
110
     */
111
    public function active()
112
    {
113
        return $this->active;
114
    }
115
116
    /**
117
     * @param integer $position The position (for ordering purpose).
118
     * @throws InvalidArgumentException If the position is not an integer (or numeric integer string).
119
     * @return Content Chainable
120
     */
121
    public function setPosition($position)
122
    {
123
        if ($position === null) {
124
            $this->position = null;
125
            return $this;
126
        }
127
        if (!is_numeric($position)) {
128
            throw new InvalidArgumentException(
129
                'Position must be an integer.'
130
            );
131
        }
132
        $this->position = (int)$position;
133
        return $this;
134
    }
135
136
    /**
137
     * @return integer
138
     */
139
    public function position()
140
    {
141
        return $this->position;
142
    }
143
144
    /**
145
     * @param \DateTime|string|null $created The date/time at object's creation.
146
     * @throws InvalidArgumentException If the date/time is invalid.
147
     * @return Content Chainable
148
     */
149 View Code Duplication
    public function setCreated($created)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
150
    {
151
        if ($created === null) {
152
            $this->created = null;
153
            return $this;
154
        }
155
        if (is_string($created)) {
156
            $created = new DateTime($created);
157
        }
158
        if (!($created instanceof DateTime)) {
159
            throw new InvalidArgumentException(
160
                'Invalid "Created" value. Must be a date/time string or a DateTime object.'
161
            );
162
        }
163
        $this->created = $created;
164
        return $this;
165
    }
166
167
    /**
168
     * @return DateTime|null
169
     */
170
    public function created()
171
    {
172
        return $this->created;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->created; (DateTime) is incompatible with the return type declared by the interface Charcoal\Object\ContentInterface::created of type Charcoal\Object\DateTime|null.

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...
173
    }
174
175
    /**
176
     * @param mixed $createdBy The creator of the content object.
177
     * @return Content Chainable
178
     */
179
    public function setCreatedBy($createdBy)
180
    {
181
        $this->createdBy = $createdBy;
182
        return $this;
183
    }
184
185
    /**
186
     * @return mixed
187
     */
188
    public function createdBy()
189
    {
190
        return $this->createdBy;
191
    }
192
193
    /**
194
     * @param \DateTime|string|null $lastModified The last modified date/time.
195
     * @throws InvalidArgumentException If the date/time is invalid.
196
     * @return Content Chainable
197
     */
198 View Code Duplication
    public function setLastModified($lastModified)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199
    {
200
        if ($lastModified === null) {
201
            $this->lastModified = null;
202
            return $this;
203
        }
204
        if (is_string($lastModified)) {
205
            $lastModified = new DateTime($lastModified);
206
        }
207
        if (!($lastModified instanceof DateTime)) {
208
            throw new InvalidArgumentException(
209
                'Invalid "Last Modified" value. Must be a date/time string or a DateTime object.'
210
            );
211
        }
212
        $this->lastModified = $lastModified;
213
        return $this;
214
    }
215
216
    /**
217
     * @return DateTime
218
     */
219
    public function lastModified()
220
    {
221
        return $this->lastModified;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->lastModified; (DateTime) is incompatible with the return type declared by the interface Charcoal\Object\ContentInterface::lastModified of type Charcoal\Object\DateTime.

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...
222
    }
223
224
    /**
225
     * @param mixed $lastModifiedBy The last modification's username.
226
     * @return Content Chainable
227
     */
228
    public function setLastModifiedBy($lastModifiedBy)
229
    {
230
        $this->lastModifiedBy = $lastModifiedBy;
231
        return $this;
232
    }
233
234
    /**
235
     * @return mixed
236
     */
237
    public function lastModifiedBy()
238
    {
239
        return $this->lastModifiedBy;
240
    }
241
242
    /**
243
     * StorableTrait > preSavƒe(): Called automatically before saving the object to source.
244
     * For content object, set the `created` and `lastModified` properties automatically
245
     * @return boolean
246
     */
247
    public function preSave()
248
    {
249
        parent::preSave();
250
251
        $this->setCreated('now');
252
        $this->setLastModified('now');
253
254
        return true;
255
    }
256
257
    /**
258
     * StorableTrait > preUpdate(): Called automatically before updating the object to source.
259
     * For content object, set the `lastModified` property automatically.
260
     *
261
     * @param array $properties The properties (ident) set for update.
262
     * @return boolean
263
     */
264
    public function preUpdate(array $properties = null)
265
    {
266
        parent::preUpdate($properties);
0 ignored issues
show
Bug introduced by
It seems like $properties defined by parameter $properties on line 264 can also be of type array; however, Charcoal\Model\AbstractModel::preUpdate() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
267
268
        // Content is revisionable
269
        if ($this->revisionEnabled()) {
270
            $this->generateRevision();
271
        }
272
273
        $this->setLastModified('now');
274
275
        return true;
276
    }
277
}
278