Completed
Push — master ( 3ee305...36a69b )
by Ryan
07:04
created

EloquentCriteria::sum()   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
dl 0
loc 4
rs 10
c 1
b 0
f 1
cc 1
eloc 2
nc 1
nop 1
1
<?php namespace Anomaly\Streams\Platform\Model;
2
3
use Anomaly\Streams\Platform\Entry\Contract\EntryInterface;
4
use Anomaly\Streams\Platform\Entry\EntryPresenter;
5
use Anomaly\Streams\Platform\Support\Collection;
6
use Anomaly\Streams\Platform\Support\Decorator;
7
use Anomaly\Streams\Platform\Support\Presenter;
8
use Anomaly\Streams\Platform\Traits\Hookable;
9
use Illuminate\Database\Eloquent\Builder;
10
use Illuminate\Foundation\Bus\DispatchesJobs;
11
12
/**
13
 * Class EloquentCriteria
14
 *
15
 * @link          http://anomaly.is/streams-platform
16
 * @author        AnomalyLabs, Inc. <[email protected]>
17
 * @author        Ryan Thompson <[email protected]>
18
 * @package       Anomaly\Streams\Platform\Model
19
 */
20
class EloquentCriteria
21
{
22
23
    use Hookable;
24
    use DispatchesJobs;
25
26
    /**
27
     * Safe builder methods.
28
     *
29
     * @var array
30
     */
31
    private $disabled = [
32
        'delete',
33
        'update',
34
    ];
35
36
    /**
37
     * The query builder.
38
     *
39
     * @var Builder|\Illuminate\Database\Query\Builder
40
     */
41
    protected $query;
42
43
    /**
44
     * Set the get method.
45
     *
46
     * @var string
47
     */
48
    protected $method;
49
50
    /**
51
     * Create a new EntryCriteria instance.
52
     *
53
     * @param Builder $query
54
     * @param string  $method
55
     */
56
    public function __construct(Builder $query, $method = 'get')
57
    {
58
        $this->query  = $query;
59
        $this->method = $method;
60
    }
61
62
    /**
63
     * Get the paginated entries.
64
     *
65
     * @param array $columns
66
     * @return Collection|Presenter|EntryPresenter
67
     */
68
    public function paginate($perPage = 15, array $columns = ['*'])
69
    {
70
        return (new Decorator())->decorate($this->query->paginate($perPage, $columns));
71
    }
72
73
    /**
74
     * Get the entries.
75
     *
76
     * @param array $columns
77
     * @return Collection|Presenter|EntryPresenter
78
     */
79
    public function get(array $columns = ['*'])
80
    {
81
        return (new Decorator())->decorate($this->query->{$this->method}($columns));
82
    }
83
84
    /**
85
     * Get the aggregate sum.
86
     *
87
     * @param array $columns
88
     * @return int
89
     */
90
    public function sum(array $columns = ['*'])
91
    {
92
        return (new Decorator())->decorate($this->query->sum($columns));
0 ignored issues
show
Documentation introduced by
$columns is of type array, but the function expects a string.

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...
Bug introduced by
The method sum does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
93
    }
94
95
    /**
96
     * Get the entry count.
97
     *
98
     * @param array $columns
99
     * @return int
100
     */
101
    public function count(array $columns = ['*'])
102
    {
103
        return (new Decorator())->decorate($this->query->count($columns));
0 ignored issues
show
Documentation introduced by
$columns is of type array, but the function expects a string.

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...
Bug introduced by
The method count does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
104
    }
105
106
    /**
107
     * Find an entry.
108
     *
109
     * @param       $identifier
110
     * @param array $columns
111
     * @return Presenter|EntryPresenter
112
     */
113
    public function find($identifier, array $columns = ['*'])
114
    {
115
        return (new Decorator())->decorate($this->query->find($identifier, $columns));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return (new \Anomaly\Str...identifier, $columns)); (object|integer|double|string|null|boolean|array) is incompatible with the return type documented by Anomaly\Streams\Platform...\EloquentCriteria::find of type Anomaly\Streams\Platform\Support\Presenter|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...
116
    }
117
118
    /**
119
     * Find an entry by column value.
120
     *
121
     * @param       $column
122
     * @param       $value
123
     * @param array $columns
124
     * @return Presenter|EntryPresenter
125
     */
126
    public function findBy($column, $value, array $columns = ['*'])
127
    {
128
        $this->query->where($column, $value);
129
130
        return (new Decorator())->decorate($this->query->first($columns));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return (new \Anomaly\Str...uery->first($columns)); (object|integer|double|string|null|boolean|array) is incompatible with the return type documented by Anomaly\Streams\Platform...loquentCriteria::findBy of type Anomaly\Streams\Platform\Support\Presenter|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...
131
    }
132
133
    /**
134
     * Return the first entry.
135
     *
136
     * @param array $columns
137
     * @return EloquentModel|EntryInterface
138
     */
139
    public function first(array $columns = ['*'])
140
    {
141
        return (new Decorator())->decorate($this->query->first($columns));
142
    }
143
144
    /**
145
     * Return whether the method is safe or not.
146
     *
147
     * @param $name
148
     * @return bool
149
     */
150
    protected function methodIsSafe($name)
151
    {
152
        return (!in_array($name, $this->disabled));
153
    }
154
155
    /**
156
     * Route through __call.
157
     *
158
     * @param $name
159
     * @return Builder|null
160
     */
161
    function __get($name)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
162
    {
163
        return $this->__call($name, []);
164
    }
165
166
    /**
167
     * Call the method on the query.
168
     *
169
     * @param $name
170
     * @param $arguments
171
     * @return Builder|null
172
     */
173
    function __call($name, $arguments)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
174
    {
175
176
        if ($this->hasHook($name)) {
177
            return $this->call($name, $arguments);
178
        }
179
180
        if ($this->methodIsSafe($name)) {
181
            call_user_func_array([$this->query, $name], $arguments);
182
183
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Anomaly\Streams\Platform\Model\EloquentCriteria) is incompatible with the return type documented by Anomaly\Streams\Platform...loquentCriteria::__call of type Illuminate\Database\Eloquent\Builder|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...
184
        }
185
186 View Code Duplication
        if (starts_with($name, 'findBy') && $column = snake_case(substr($name, 6))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
187
188
            call_user_func_array([$this->query, 'where'], array_merge([$column], $arguments));
189
190
            return $this->first();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->first(); (object|integer|double|string|null|boolean|array) is incompatible with the return type documented by Anomaly\Streams\Platform...loquentCriteria::__call of type Illuminate\Database\Eloquent\Builder|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...
191
        }
192
193 View Code Duplication
        if (starts_with($name, 'where') && $column = snake_case(substr($name, 5))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
194
            call_user_func_array([$this->query, 'where'], array_merge([$column], $arguments));
195
196
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Anomaly\Streams\Platform\Model\EloquentCriteria) is incompatible with the return type documented by Anomaly\Streams\Platform...loquentCriteria::__call of type Illuminate\Database\Eloquent\Builder|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...
197
        }
198
199
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Anomaly\Streams\Platform\Model\EloquentCriteria) is incompatible with the return type documented by Anomaly\Streams\Platform...loquentCriteria::__call of type Illuminate\Database\Eloquent\Builder|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...
200
    }
201
202
    /**
203
     * Return the string.
204
     *
205
     * @return string
206
     */
207
    public function __toString()
208
    {
209
        return '';
210
    }
211
}