Completed
Push — dev ( 46c257...e6e7dc )
by Zach
03:51
created

Project::linkText()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Larafolio\Models;
4
5
use Larafolio\Models\Image;
6
use Larafolio\Models\Project;
7
use Larafolio\Helpers\Sluggable;
8
use Larafolio\Models\HasContent;
9
use Illuminate\Database\Eloquent\SoftDeletes;
10
11
class Project extends HasContent
12
{
13
    use Sluggable, SoftDeletes;
14
15
    /**
16
     * The table associated with the model.
17
     *
18
     * @var string
19
     */
20
    protected $table = 'projects';
21
22
    /**
23
     * The attributes that are mass assignable.
24
     *
25
     * @var array
26
     */
27
    protected $fillable = [
28
        'name', 'slug', 'type', 'visible', 'order',
29
    ];
30
31
    /**
32
     * Properties to always eager load.
33
     *
34
     * @var array
35
     */
36
    protected $with = ['blocks', 'images', 'links'];
37
38
    /**
39
     * The attributes that should be casted to native types.
40
     *
41
     * @var array
42
     */
43
    protected $casts = [
44
        'visible' => 'boolean',
45
    ];
46
47
    /**
48
     * Fields that are dates.
49
     *
50
     * @var array
51
     */
52
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
53
54
    /**
55
     * Get the route key for the model.
56
     *
57
     * @return string
58
     */
59
    public function getRouteKeyName()
60
    {
61
        return 'slug';
62
    }
63
64
    /**
65
     * Bootstrap model.
66
     */
67
    public static function boot()
68
    {
69
        parent::boot();
70
71
        static::creating(function (Project $project) {
72
            $project->setSlug('name');
73
        });
74
75
        static::updating(function (Project $project) {
76
            $project->setSlug('name');
77
        });
78
    }
79
80
    /**
81
     * Return all visible projects.
82
     *
83
     * @param bool $group If true, group projects by 'type'.
84
     * @param bool $order If true, order projects by 'order'.
85
     *
86
     * @return \Illuminate\Support\Collection
87
     */
88
    public static function allVisible($group = true, $order = true)
89
    {
90
        $query = static::where('visible', true);
91
92
        return static::orderAndGroupQuery($query, $group, $order);
93
    }
94
95
    /**
96
     * Return all hidden projects.
97
     *
98
     * @param bool $group If true, group projects by 'type'.
99
     * @param bool $order If true, order projects by 'order'.
100
     *
101
     * @return \Illuminate\Support\Collection
102
     */
103
    public static function allHidden($group = true, $order = true)
104
    {
105
        $query = static::where('visible', false);
106
107
        return static::orderAndGroupQuery($query, $group, $order);
108
    }
109
110
    /**
111
     * Return all projects grouped by 'type'.
112
     *
113
     * @param bool $order If true, order projects by 'order'.
114
     *
115
     * @return \Illuminate\Support\Collection
116
     */
117
    public static function allGrouped($order = true)
118
    {
119
        $query = static::query();
120
121
        return static::orderAndGroupQuery($query, true, $order);
0 ignored issues
show
Documentation introduced by
$query is of type object<Illuminate\Database\Eloquent\Builder>, but the function expects a object<Larafolio\Models\Builder>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
122
    }
123
124
    /**
125
     * Return all projects ordered by 'order'.
126
     *
127
     * @return \Illuminate\Support\Collection
128
     */
129
    public static function allOrdered()
130
    {
131
        $query = static::query();
132
133
        return static::orderAndGroupQuery($query, false, true);
0 ignored issues
show
Documentation introduced by
$query is of type object<Illuminate\Database\Eloquent\Builder>, but the function expects a object<Larafolio\Models\Builder>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
134
    }
135
136
    /**
137
     * Order and group query, return results.
138
     *
139
     * @param Builder $query Query to be ordered.
140
     * @param bool    $group If true, group projects by 'type'.
141
     * @param bool    $order If true, order projects by 'order'.
142
     *
143
     * @return \Illuminate\Support\Collection
144
     */
145
    protected static function orderAndGroupQuery($query, $group, $order)
146
    {
147
        if ($order) {
148
            $query->orderBy('order');
149
        }
150
151
        $query->orderRelationship('links');
152
153
        $query->orderRelationship('blocks');
154
155
        if ($group) {
156
            return $query->get()
157
                ->each(function ($project, $key) {
158
                    $project->index = $key;
159
                })
160
                ->groupBy('type');
161
        }
162
163
        return $query->get();
164
    }
165
166
    /**
167
     * Get all projects with given block name.
168
     *
169
     * @param string $blockName Name of block.
170
     *
171
     * @return \Illuminate\Support\Collection
172
     */
173
    public static function hasBlockNamed($blockName)
174
    {
175
        return static::hasRelationshipNamed('text_blocks', $blockName);
176
    }
177
178
    /**
179
     * Get all projects with given image name.
180
     *
181
     * @param string $imageName Name of image.
182
     *
183
     * @return \Illuminate\Support\Collection
184
     */
185
    public static function hasImageNamed($imageName)
186
    {
187
        return static::hasRelationshipNamed('images', $imageName);
188
    }
189
190
    /**
191
     * Get all projects with given link name.
192
     *
193
     * @param string $linkName Name of link.
194
     *
195
     * @return \Illuminate\Support\Collection
196
     */
197
    public static function hasLinkNamed($linkName)
198
    {
199
        return static::hasRelationshipNamed('links', $linkName);
200
    }
201
202
    /**
203
     * Get all projects with relationship on table that has given name.
204
     *
205
     * @param string $table Name of table relationship is on.
206
     * @param string $name  Relationship name.
207
     *
208
     * @return \Illuminate\Support\Collection
209
     */
210
    protected static function hasRelationshipNamed($table, $name)
211
    {
212
        return static::join($table, 'projects.id', '=', "{$table}.project_id")
213
            ->where("{$table}.name", '=', $name)
214
            ->select('projects.*')
215
            ->get();
216
    }
217
218
    /**
219
     * Return the project id.
220
     *
221
     * @return int
222
     */
223
    public function id()
224
    {
225
        return $this->id;
1 ignored issue
show
Documentation introduced by
The property id does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
226
    }
227
228
    /**
229
     * Return the project name.
230
     *
231
     * @return string
232
     */
233
    public function name()
234
    {
235
        return $this->name;
1 ignored issue
show
Documentation introduced by
The property name does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
236
    }
237
238
    /**
239
     * Return the project type.
240
     *
241
     * @return string
242
     */
243
    public function type()
244
    {
245
        return $this->type;
1 ignored issue
show
Documentation introduced by
The property type does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
246
    }
247
248
    /**
249
     * Return the project slug.
250
     *
251
     * @return string
252
     */
253
    public function slug()
254
    {
255
        return $this->slug;
1 ignored issue
show
Documentation introduced by
The property slug does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
256
    }
257
258
    /**
259
     * Return the project order value.
260
     *
261
     * @return int
262
     */
263
    public function order()
264
    {
265
        return $this->order;
1 ignored issue
show
Documentation introduced by
The property order does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
266
    }
267
268
    /**
269
     * Get formatted text of block named description or first block.
270
     *
271
     * @return Larafolio\Models\TextBlock
272
     */
273
    public function getProjectBlock()
274
    {
275
        $block = $this->block($this->name());
276
277
        if ($block) {
278
            return $block;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $block; (Larafolio\Models\Traits\Larafolio\Models\TextBlock) is incompatible with the return type documented by Larafolio\Models\Project::getProjectBlock of type Larafolio\Models\Larafolio\Models\TextBlock.

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...
279
        } elseif ($this->hasBlocks()) {
280
            return $this->blocks()->first();
281
        }
282
    }
283
284
    /**
285
     * Get formatted text of block named description or first block.
286
     *
287
     * @param bool $formatted If true, return formatted text.
288
     *
289
     * @return string
290
     */
291
    public function getProjectBlockText($formatted = true)
292
    {
293
        $project = $this->getProjectBlock();
294
295
        if ($project && $formatted) {
296
            return $project->formattedText();
297
        } elseif ($project) {
298
            return $project->text();
299
        }
300
301
        return $project;
302
    }
303
304
    /**
305
     * Get url of small image with project name or first image in collection.
306
     *
307
     * @return string
308
     */
309
    public function getProjectImage()
310
    {
311
        $projectImage = $this->image($this->name());
312
313
        if ($projectImage) {
314
            return $projectImage;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $projectImage; (Larafolio\Models\Traits\Larafolio\Models\Image) is incompatible with the return type documented by Larafolio\Models\Project::getProjectImage of type string.

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...
315
        } elseif ($this->hasImages()) {
316
            return $this->images()->first();
317
        }
318
    }
319
320
    /**
321
     * Get url of small image with project name or first image in collection.
322
     *
323
     * @param string $size The size of the image, name of image cache filter.
324
     *
325
     * @return string
326
     */
327
    public function getProjectImageUrl($size = 'small')
328
    {
329
        $projectImage = $this->getProjectImage();
330
331
        if ($projectImage) {
332
            return $projectImage->{$size}();
0 ignored issues
show
Bug introduced by
The method $size cannot be called on $projectImage (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
333
        }
334
335
        return $projectImage;
336
    }
337
338
    /**
339
     * Get blocks sorted by order.
340
     *
341
     * @param \Builder $query Query builder.
342
     * @param string   $slug  Project slug.
343
     *
344
     * @return \Builder
345
     */
346
    public function scopeWithBlocks($query, $slug)
347
    {
348
        return $query->orderRelationship('blocks')
349
            ->where('slug', $slug);
350
    }
351
352
    /**
353
     * Get full project info (blocks and links sorted by order).
354
     *
355
     * @param \Builder $query Query builder.
356
     * @param string   $slug  Project slug.
357
     *
358
     * @return \Builder
359
     */
360
    public function scopeFull($query, $slug)
361
    {
362
        return $query->orderRelationship('blocks')
363
            ->orderRelationship('links')
364
            ->where('slug', $slug);
365
    }
366
367
    /**
368
     * Return images with all props needed for javascript.
369
     *
370
     * @return Collection
371
     */
372
    public function imagesWithProps()
373
    {
374
        return $this->images
1 ignored issue
show
Documentation introduced by
The property images does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
375
            ->map(function (Image $image) {
376
                return $image->generateProps();
377
            })->reverse()->values();
378
    }
379
380
    /**
381
     * Order given relationship by order value.
382
     *
383
     * @param \Builder $query        Query builder.
384
     * @param string   $relationship Name of relationship to order.
385
     *
386
     * @return \Builder
387
     */
388
    public function scopeOrderRelationship($query, $relationship)
389
    {
390
        return $query->with([$relationship => function ($query) {
391
            $query->orderBy('order');
392
        }]);
393
    }
394
395
    /**
396
     * Return project properties to be passed to js.
397
     *
398
     * @return array
399
     */
400
    public function generateProps()
401
    {
402
        return [
403
            'deletedAt' => $this->deleted_at->diffForHumans(),
1 ignored issue
show
Documentation introduced by
The property deleted_at does not exist on object<Larafolio\Models\Project>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
404
            'id'        => $this->id(),
405
            'name'      => $this->name(),
406
            'slug'      => $this->slug(),
407
        ];
408
    }
409
}
410