Test Setup Failed
Push — master ( 26a157...82ba33 )
by Vitaliy
07:02 queued 34s
created

Column::getGrid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace ViewComponents\Grids\Component;
4
5
use mp;
6
use Nayjest\Tree\ChildNodeTrait;
7
use ViewComponents\Grids\Grid;
8
use ViewComponents\ViewComponents\Base\ComponentInterface;
9
use ViewComponents\ViewComponents\Base\Compound\PartInterface;
10
use ViewComponents\ViewComponents\Base\Compound\PartTrait;
11
use ViewComponents\ViewComponents\Base\ContainerComponentInterface;
12
use ViewComponents\ViewComponents\Base\DataViewComponentInterface;
13
use ViewComponents\ViewComponents\Component\Compound;
14
use ViewComponents\ViewComponents\Component\Html\Tag;
15
use ViewComponents\ViewComponents\Component\DataView;
16
use ViewComponents\ViewComponents\Component\Part;
17
18
/**
19
 * Grid column.
20
 */
21
class Column implements PartInterface
22
{
23
    use PartTrait {
24
        PartTrait::attachToCompound as attachToCompoundInternal;
25
    }
26
    use ChildNodeTrait;
27
28
    /**
29
     * Text label that will be rendered in table header.
30
     *
31
     * @var string|null
32
     */
33
    protected $label;
34
35
    /** @var  ComponentInterface */
36
    protected $dataCell;
37
38
    /** @var  ComponentInterface */
39
    protected $titleCell;
40
41
    /** @var ComponentInterface */
42
    protected $titleView;
43
44
    /** @var ComponentInterface */
45
    protected $dataView;
46
47
    /** @var  Part|null */
48
    protected $titleCellPart;
49
50
    /** @var  Part|null */
51
    protected $dataCellPart;
52
53
    /** @var  string|null */
54
    protected $dataFieldName;
55
56
    /** @var  callable|null */
57
    protected $valueCalculator;
58
59
    /** @var  callable|null */
60
    protected $valueFormatter;
61
62
    /**
63
     * Constructor.
64
     *
65
     * @param string|null $columnId unique column name for internal usage
66
     * @param string|null $label column label
67
     */
68
    public function __construct($columnId, $label = null)
69
    {
70
        $this->setDestinationParentId(Compound::ROOT_ID);
71
        $this->setId($columnId);
72
        $this->setLabel($label);
73
        $this->titleView = new DataView([$this, 'getLabel']);
74
        $this->dataView = new DataView([$this, 'getCurrentValueFormatted']);
75
    }
76
77
    /**
78
     * Returns formatted value of current data cell.
79
     *
80
     * @return string
81
     */
82
    public function getCurrentValueFormatted()
83
    {
84
        return $this->formatValue($this->getCurrentValue());
85
    }
86
87
    /**
88
     * Formats value extracted from data row.
89
     *
90
     * @param $value
91
     * @return string
92
     */
93
    public function formatValue($value)
94
    {
95
        $formatter = $this->getValueFormatter();
96
        return (string)($formatter ? call_user_func($formatter, $value, $this->getGrid()->getCurrentRow()) : $value);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ViewComponents\ViewComponents\Component\Compound as the method getCurrentRow() does only exist in the following sub-classes of ViewComponents\ViewComponents\Component\Compound: ViewComponents\Grids\Grid. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
97
    }
98
99
    /**
100
     * Returns current data cell value.
101
     *
102
     * @return mixed
103
     */
104
    public function getCurrentValue()
105
    {
106
        $func = $this->getValueCalculator();
107
        if ($func !== null) {
108
            return call_user_func($func, $this->getGrid()->getCurrentRow());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ViewComponents\ViewComponents\Component\Compound as the method getCurrentRow() does only exist in the following sub-classes of ViewComponents\ViewComponents\Component\Compound: ViewComponents\Grids\Grid. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
109
        } else {
110
            return mp\getValue($this->getGrid()->getCurrentRow(), $this->getDataFieldName());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class ViewComponents\ViewComponents\Component\Compound as the method getCurrentRow() does only exist in the following sub-classes of ViewComponents\ViewComponents\Component\Compound: ViewComponents\Grids\Grid. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
111
        }
112
    }
113
114
    /**
115
     * Returns component that renders data (content of 'td').
116
     *
117
     * @return DataViewComponentInterface
118
     */
119
    public function getDataView()
120
    {
121
        return $this->dataView;
122
    }
123
124
    /**
125
     * Returns view component that displays column title.
126
     *
127
     * @return DataViewComponentInterface
128
     */
129
    public function getTitleView()
130
    {
131
        return $this->titleView;
132
    }
133
134
    /**
135
     * Sets name of associated field in data rows returned from data provider.
136
     *
137
     * @param string|null $dataFieldName
138
     * @return $this
139
     */
140
    public function setDataFieldName($dataFieldName)
141
    {
142
        $this->dataFieldName = $dataFieldName;
143
        return $this;
144
    }
145
146
    /**
147
     * Returns name of associated field in data rows returned from data provider.
148
     *
149
     * @return string
150
     */
151
    public function getDataFieldName()
152
    {
153
        return $this->dataFieldName ?: $this->getId();
154
    }
155
156
    /**
157
     * Returns function calculating column value.
158
     *
159
     * This function accepts data row as first argument.
160
     *
161
     * @return callable|null
162
     */
163
    public function getValueCalculator()
164
    {
165
        return $this->valueCalculator;
166
    }
167
168
    /**
169
     * Sets function for calculating column value.
170
     *
171
     * This function accepts data row as first argument.
172
     *
173
     * @param $valueCalculator
174
     * @return $this
175
     */
176
    public function setValueCalculator(callable $valueCalculator = null)
177
    {
178
        $this->valueCalculator = $valueCalculator;
179
        return $this;
180
    }
181
182
    /**
183
     * Sets function for column value formatting.
184
     *
185
     * @param callable|null $valueFormatter
186
     * @return Column
187
     */
188
    public function setValueFormatter($valueFormatter)
189
    {
190
        $this->valueFormatter = $valueFormatter;
191
        return $this;
192
    }
193
194
    /**
195
     * Returns function fir column value formatting.
196
     *
197
     * @return callable|null
198
     */
199
    public function getValueFormatter()
200
    {
201
        return $this->valueFormatter;
202
    }
203
204
    /**
205
     * Sets component for rendering table cell with data (<td>).
206
     *
207
     * @param ContainerComponentInterface $cell
208
     * @return $this
209
     */
210
    public function setDataCell(ContainerComponentInterface $cell)
211
    {
212
        $this->dataCell = $cell;
213
        $this->dataCell->children()->add($this->dataView, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nayjest\Collection\CollectionReadInterface as the method add() does only exist in the following implementations of said interface: Nayjest\Collection\Collection, Nayjest\Collection\Exten...ctTypedObjectCollection, Nayjest\Collection\Extended\LazyLoadCollection, Nayjest\Collection\Extended\ObjectCollection, Nayjest\Tree\NodeCollection, ViewComponents\ViewCompo...ata\OperationCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
214
        if ($this->dataCellPart !== null) {
215
            $this->dataCellPart->setView($cell);
216
        }
217
        return $this;
218
    }
219
220
    /**
221
     * Returns title cell component ('th' tag).
222
     *
223
     * @return ComponentInterface
224
     */
225
    public function getTitleCell()
226
    {
227
        if ($this->titleCell === null) {
228
            $this->setTitleCell(new Tag('th'));
229
        }
230
        return $this->titleCell;
231
    }
232
233
    /**
234
     * Returns component that renders data cell ('td' tag).
235
     *
236
     * @return ComponentInterface
237
     */
238
    public function getDataCell()
239
    {
240
        if ($this->dataCell === null) {
241
            $this->setDataCell(
242
                new Tag('td')
243
            );
244
        }
245
        return $this->dataCell;
246
    }
247
248
    /**
249
     * Sets title cell component ('th' tag).
250
     *
251
     * @param ContainerComponentInterface $cell
252
     * @return $this
253
     */
254
    public function setTitleCell(ContainerComponentInterface $cell)
255
    {
256
        $this->titleCell = $cell;
257
        $this->titleCell->children()->add($this->titleView, true);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nayjest\Collection\CollectionReadInterface as the method add() does only exist in the following implementations of said interface: Nayjest\Collection\Collection, Nayjest\Collection\Exten...ctTypedObjectCollection, Nayjest\Collection\Extended\LazyLoadCollection, Nayjest\Collection\Extended\ObjectCollection, Nayjest\Tree\NodeCollection, ViewComponents\ViewCompo...ata\OperationCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
258
        if ($this->titleCellPart !== null) {
259
            $this->titleCellPart->setView($cell);
260
        }
261
        return $this;
262
    }
263
264
    /**
265
     * Returns text label that will be rendered in table header.
266
     *
267
     * @return string
268
     */
269
    public function getLabel()
270
    {
271
        if ($this->label === null) {
272
            $this->label = ucwords(str_replace(array('-', '_', '.'), ' ', $this->id));
273
        }
274
        return $this->label;
275
    }
276
277
    /**
278
     * Sets text label that will be rendered in table header.
279
     *
280
     * @param string|null $label
281
     * @return $this
282
     */
283
    public function setLabel($label)
284
    {
285
        $this->label = $label;
286
        return $this;
287
    }
288
289
    /**
290
     * @param Compound $root
291
     */
292
    public function attachToCompound(Compound $root)
293
    {
294
        $this->attachToCompoundInternal($root);
295
        $parts = $root->getComponents();
296
        $titleCellPart = $this->getTitleCellPart();
297
        if (!$parts->contains($titleCellPart)) {
298
            $parts->add($titleCellPart);
299
        }
300
        $dataCellPart = $this->getDataCellPart();
301
        if (!$parts->contains($dataCellPart)) {
302
            $parts->add($dataCellPart);
303
        }
304
    }
305
306
    /**
307
     * @return Part
308
     */
309
    protected function getDataCellPart()
310
    {
311
        if ($this->dataCellPart === null) {
312
            $this->dataCellPart = new Part(
313
                $this->getDataCell(),
0 ignored issues
show
Documentation introduced by
$this->getDataCell() is of type object<ViewComponents\Vi...ase\ComponentInterface>, but the function expects a null|object<ViewComponen...ViewComponentInterface>.

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...
314
                'column-' . $this->getId() . '-data-cell',
315
                'record_view'
316
            );
317
        }
318
        return $this->dataCellPart;
319
    }
320
321
    /**
322
     * @return Part
323
     */
324
    protected function getTitleCellPart()
325
    {
326
        if ($this->titleCellPart === null) {
327
            $this->titleCellPart = new Part(
328
                $this->titleCellPart = $this->getTitleCell(),
0 ignored issues
show
Documentation introduced by
$this->titleCellPart = $this->getTitleCell() is of type object<ViewComponents\Vi...ase\ComponentInterface>, but the function expects a null|object<ViewComponen...ViewComponentInterface>.

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...
Documentation Bug introduced by
It seems like $this->getTitleCell() of type object<ViewComponents\Vi...ase\ComponentInterface> is incompatible with the declared type object<ViewComponents\Vi...ts\Component\Part>|null of property $titleCellPart.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
329
                'column-' . $this->getId() . '-title-cell',
330
                'title_row'
331
            );
332
        }
333
        return $this->titleCellPart;
334
    }
335
336
    /**
337
     * @return null|Grid
338
     */
339
    protected function getGrid()
340
    {
341
        return $this->root;
342
    }
343
}
344