Completed
Push — master ( 5c8f9b...c72a1f )
by Christopher
01:11
created

PostBuilder::whereCategories()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.4266
c 0
b 0
f 0
cc 7
nc 7
nop 1
1
<?php
2
3
namespace Chriscreates\Blog\Builders;
4
5
use Carbon\Carbon;
6
use Chriscreates\Blog\Category;
7
use Chriscreates\Blog\Post;
8
use Illuminate\Support\Collection;
9
10
class PostBuilder extends Builder
11
{
12
    /**
13
     * Return results where Posts have status.
14
     *
15
     * @param string $status
16
     * @return \Chriscreates\Blog\Builders\PostBuilder
17
     */
18
    public function status(string $status) : PostBuilder
19
    {
20
        return $this->where('status', $status);
21
    }
22
23
    /**
24
     * Return results where Posts have been published.
25
     *
26
     * @return \Chriscreates\Blog\Builders\PostBuilder
27
     */
28
    public function published() : PostBuilder
29
    {
30
        return $this->whereIn('status', [Post::PUBLISHED, Post::SCHEDULED])
0 ignored issues
show
Documentation Bug introduced by
The method whereIn does not exist on object<Chriscreates\Blog\Builders\PostBuilder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
31
        ->where('published_at', '<=', Carbon::now());
32
    }
33
34
    /**
35
     * Return results where Posts have been scheduled to be published.
36
     *
37
     * @return \Chriscreates\Blog\Builders\PostBuilder
38
     */
39
    public function scheduled() : PostBuilder
40
    {
41
        return $this->where(function ($query) {
42
            return $query->where('status', Post::SCHEDULED)
43
          ->where('published_at', '>', Carbon::now());
44
        });
45
    }
46
47
    /**
48
     * Return results where Posts are drafted.
49
     *
50
     * @return \Chriscreates\Blog\Builders\PostBuilder
51
     */
52
    public function draft() : PostBuilder
53
    {
54
        return $this->where(function ($query) {
55
            return $query->where('status', Post::DRAFT)
56
          ->whereNull('published_at');
57
        });
58
    }
59
60
    /**
61
     * Return results where Posts are not yet published.
62
     *
63
     * @return \Chriscreates\Blog\Builders\PostBuilder
64
     */
65
    public function notPublished() : PostBuilder
66
    {
67
        return $this->where(function ($query) {
68
            return $query->draft();
69
        })->orWhere(function ($query) {
70
            return $query->scheduled();
71
        });
72
    }
73
74
    /**
75
     * Order Post results by latest published.
76
     *
77
     * @return \Chriscreates\Blog\Builders\PostBuilder
78
     */
79
    public function orderByLatest() : PostBuilder
80
    {
81
        return $this->orderBy('published_at', 'DESC');
0 ignored issues
show
Bug introduced by
The method orderBy() does not exist on Chriscreates\Blog\Builders\PostBuilder. Did you maybe mean orderByLatest()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
82
    }
83
84
    /**
85
     * Return results where Posts have been published last month.
86
     *
87
     * @return \Chriscreates\Blog\Builders\PostBuilder
88
     */
89
    public function publishedLastMonth() : PostBuilder
90
    {
91
        return $this->whereBetween('published_at', [
0 ignored issues
show
Documentation Bug introduced by
The method whereBetween does not exist on object<Chriscreates\Blog\Builders\PostBuilder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
92
            Carbon::now()->subMonth(), Carbon::now(),
93
        ])->orderByLatest()->limit($limit);
0 ignored issues
show
Bug introduced by
The variable $limit does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
94
    }
95
96
    /**
97
     * Return results where Posts have been published last week.
98
     *
99
     * @return \Chriscreates\Blog\Builders\PostBuilder
100
     */
101
    public function publishedLastWeek() : PostBuilder
102
    {
103
        return $this->whereBetween('published_at', [
0 ignored issues
show
Documentation Bug introduced by
The method whereBetween does not exist on object<Chriscreates\Blog\Builders\PostBuilder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
104
            Carbon::now()->subWeek(), Carbon::now(),
105
        ])->orderByLatest();
106
    }
107
108
    /**
109
     * Return results where Posts are related by the passed in Post Tags.
110
     *
111
     * @param \Chriscreates\Blog\Post $post
112
     * @return \Chriscreates\Blog\Builders\PostBuilder
113
     */
114
    public function relatedByPostTags(Post $post) : PostBuilder
115
    {
116
        return $this->whereHas('tags', function ($query) use ($post) {
0 ignored issues
show
Bug introduced by
The method where does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloq...ns\QueriesRelationships.

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...
117
            return $query->whereIn('name', $post->tags->pluck('name'));
118
        })->where('id', '!=', $post->id);
119
    }
120
121
    /**
122
     * Return results where Posts are related by the passed in Post Category.
123
     *
124
     * @param \Chriscreates\Blog\Post $post
125
     * @return \Chriscreates\Blog\Builders\PostBuilder
126
     */
127
    public function relatedByPostCategory(Post $post) : PostBuilder
128
    {
129
        return $this->whereHas('category', function ($query) use ($post) {
0 ignored issues
show
Bug introduced by
The method where does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloq...ns\QueriesRelationships.

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...
130
            return $query->where('id', $post->category->id);
131
        })->where('id', '!=', $post->id);
132
    }
133
134
    /**
135
     * Return results where Posts contain the Category(s) passed.
136
     *
137
     * @param $categories
138
     * @return \Chriscreates\Blog\Builders\PostBuilder
139
     */
140
    public function whereCategories($categories = null) : PostBuilder
141
    {
142
        // search by category name
143
        if (is_string($categories)) {
144
            return $this->whereCategory('name', $categories);
145
        }
146
147
        // search by category id
148
        if (is_int($categories)) {
149
            return $this->whereCategory('id', $categories);
150
        }
151
152
        // search by multiple categories
153
        if (is_array($categories)) {
154
            if (is_int($categories[0])) {
155
                $field = 'id';
156
            } else {
157
                $field = 'name';
158
            }
159
160
            return $this->whereCategory($field, $categories);
161
        }
162
163
        // search by category model
164
        if ($categories instanceof Category) {
165
            return $this->whereCategory('id', $categories->id);
166
        }
167
168
        // search by categories collection
169
        if ($categories instanceof Collection) {
170
            return $this->whereCategory('id', $categories->pluck('id')->toArray());
171
        }
172
173
        return $this;
174
    }
175
176
    /**
177
     * Return results where Posts contain the Category(s) passed.
178
     *
179
     * @param array $options
180
     * @return \Chriscreates\Blog\Builders\PostBuilder
181
     */
182
    public function whereCategory(...$options) : PostBuilder
183
    {
184
        $collection = collect([
185
            'field' => 'id',
186
            'operator' => '=',
187
            'value' => null,
188
        ]);
189
190
        // Search by field and value
191 View Code Duplication
        if (count($options) == 2) {
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...
192
            $collection = $collection->replace(['field' => $options[0]])
193
            ->replace(['value' => $options[1]]);
194
        }
195
196
        // Search by field, operator and value
197 View Code Duplication
        if (count($options) == 3) {
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...
198
            $collection = $collection->replace(['field' => $options[0]])
199
            ->replace(['operator' => $options[1]])
200
            ->replace(['value' => $options[2]]);
201
        }
202
203
        // $this->with('category');
204
205
        if (is_array($collection['value'])) {
206
            return $this->whereHas(
207
                'category',
208
                function ($query) use ($collection) {
209
                    return $query->whereIn(
210
                        $collection['field'],
211
                        $collection['value']
212
                    );
213
                }
214
            );
215
        }
216
217
        return $this->whereHas(
218
            'category',
219
            function ($query) use ($collection) {
220
                return $query->where(
221
                    $collection['field'],
222
                    $collection['operator'],
223
                    $collection['value']
224
                );
225
            }
226
        );
227
    }
228
}
229