Completed
Push — master ( a4bd6c...26a953 )
by Vitaliy
03:59
created

Column::setTitleCell()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0932

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 5
cts 7
cp 0.7143
rs 9.6666
cc 2
eloc 6
nc 2
nop 1
crap 2.0932
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 2
    public function __construct($columnId, $label = null)
69
    {
70 2
        $this->setDestinationParentId(Compound::ROOT_ID);
71 2
        $this->setId($columnId);
72 2
        $this->setLabel($label);
73 2
        $this->titleView = new DataView(null, [$this, 'getLabel']);
74 2
        $this->dataView = new DataView(null, [$this, 'getCurrentValueFormatted']);
75 2
    }
76
77
    /**
78
     * Returns formatted value of current data cell.
79
     *
80
     * @return string
81
     */
82 2
    public function getCurrentValueFormatted()
83
    {
84 2
        return $this->formatValue($this->getCurrentValue());
85
    }
86
87
    /**
88
     * Formats value extracted from data row.
89
     *
90
     * @param $value
91
     * @return string
92
     */
93 2
    public function formatValue($value)
94
    {
95 2
        $formatter = $this->getValueFormatter();
96 2
        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 2
    public function getCurrentValue()
105
    {
106 2
        $func = $this->getValueCalculator();
107 2
        $currentDataRow =  $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...
108 2
        if ($func !== null) {
109
            return call_user_func($func, $currentDataRow);
110
        } else {
111 2
            return mp\getValue($currentDataRow, $this->getDataFieldName());
112
        }
113
    }
114
115
    /**
116
     * Returns component that renders data (content of 'td').
117
     *
118
     * @return DataViewComponentInterface
119
     */
120
    public function getDataView()
121
    {
122
        return $this->dataView;
123
    }
124
125
    /**
126
     * Returns view component that displays column title.
127
     *
128
     * @return DataViewComponentInterface
129
     */
130
    public function getTitleView()
131
    {
132
        return $this->titleView;
133
    }
134
135
    /**
136
     * Sets name of associated field in data rows returned from data provider.
137
     *
138
     * @param string|null $dataFieldName
139
     * @return $this
140
     */
141
    public function setDataFieldName($dataFieldName)
142
    {
143
        $this->dataFieldName = $dataFieldName;
144
        return $this;
145
    }
146
147
    /**
148
     * Returns name of associated field in data rows returned from data provider.
149
     *
150
     * @return string
151
     */
152 2
    public function getDataFieldName()
153
    {
154 2
        return $this->dataFieldName ?: $this->getId();
155
    }
156
157
    /**
158
     * Returns function calculating column value.
159
     *
160
     * This function accepts data row as first argument.
161
     *
162
     * @return callable|null
163
     */
164 2
    public function getValueCalculator()
165
    {
166 2
        return $this->valueCalculator;
167
    }
168
169
    /**
170
     * Sets function for calculating column value.
171
     *
172
     * This function accepts data row as first argument.
173
     *
174
     * @param $valueCalculator
175
     * @return $this
176
     */
177
    public function setValueCalculator(callable $valueCalculator = null)
178
    {
179
        $this->valueCalculator = $valueCalculator;
180
        return $this;
181
    }
182
183
    /**
184
     * Sets function for column value formatting.
185
     *
186
     * @param callable|null $valueFormatter
187
     * @return Column
188
     */
189
    public function setValueFormatter(callable $valueFormatter = null)
190
    {
191
        $this->valueFormatter = $valueFormatter;
192
        return $this;
193
    }
194
195
    /**
196
     * Returns function fir column value formatting.
197
     *
198
     * @return callable|null
199
     */
200 2
    public function getValueFormatter()
201
    {
202 2
        return $this->valueFormatter;
203
    }
204
205
    /**
206
     * Sets component for rendering table cell with data (<td>).
207
     *
208
     * @param ContainerComponentInterface $cell
209
     * @return $this
210
     */
211 2
    public function setDataCell(ContainerComponentInterface $cell)
212
    {
213 2
        $this->dataCell = $cell;
214 2
        $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...
215 2
        if ($this->dataCellPart !== null) {
216
            $this->dataCellPart->setView($cell);
217
        }
218 2
        return $this;
219
    }
220
221
    /**
222
     * Returns title cell component ('th' tag).
223
     *
224
     * @return ComponentInterface
225
     */
226 2
    public function getTitleCell()
227
    {
228 2
        if ($this->titleCell === null) {
229 2
            $this->setTitleCell(new Tag('th'));
230 2
        }
231 2
        return $this->titleCell;
232
    }
233
234
    /**
235
     * Returns component that renders data cell ('td' tag).
236
     *
237
     * @return ComponentInterface
238
     */
239 2
    public function getDataCell()
240
    {
241 2
        if ($this->dataCell === null) {
242 2
            $this->setDataCell(
243 2
                new Tag('td')
244 2
            );
245 2
        }
246 2
        return $this->dataCell;
247
    }
248
249
    /**
250
     * Sets title cell component ('th' tag).
251
     *
252
     * @param ContainerComponentInterface $cell
253
     * @return $this
254
     */
255 2
    public function setTitleCell(ContainerComponentInterface $cell)
256
    {
257 2
        $this->titleCell = $cell;
258 2
        $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...
259 2
        if ($this->titleCellPart !== null) {
260
            $this->titleCellPart->setView($cell);
261
        }
262 2
        return $this;
263
    }
264
265
    /**
266
     * Returns text label that will be rendered in table header.
267
     *
268
     * @return string
269
     */
270 2
    public function getLabel()
271
    {
272 2
        if ($this->label === null) {
273 2
            $this->label = ucwords(str_replace(array('-', '_', '.'), ' ', $this->id));
274 2
        }
275 2
        return $this->label;
276
    }
277
278
    /**
279
     * Sets text label that will be rendered in table header.
280
     *
281
     * @param string|null $label
282
     * @return $this
283
     */
284 2
    public function setLabel($label)
285
    {
286 2
        $this->label = $label;
287 2
        return $this;
288
    }
289
290
    /**
291
     * @param Compound $root
292
     */
293 2
    public function attachToCompound(Compound $root)
294
    {
295 2
        $this->attachToCompoundInternal($root);
296 2
        $parts = $root->getComponents();
297 2
        $titleCellPart = $this->getTitleCellPart();
298 2
        if (!$parts->contains($titleCellPart)) {
299 2
            $parts->add($titleCellPart);
300 2
        }
301 2
        $dataCellPart = $this->getDataCellPart();
302 2
        if (!$parts->contains($dataCellPart)) {
303 2
            $parts->add($dataCellPart);
304 2
        }
305 2
    }
306
307
    /**
308
     * @return Part
309
     */
310 2
    protected function getDataCellPart()
311
    {
312 2
        if ($this->dataCellPart === null) {
313 2
            $this->dataCellPart = new Part(
314 2
                $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...
315 2
                'column-' . $this->getId() . '-data-cell',
316
                'record_view'
317 2
            );
318 2
        }
319 2
        return $this->dataCellPart;
320
    }
321
322
    /**
323
     * @return Part
324
     */
325 2
    protected function getTitleCellPart()
326
    {
327 2
        if ($this->titleCellPart === null) {
328 2
            $this->titleCellPart = new Part(
329 2
                $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...
330 2
                'column-' . $this->getId() . '-title-cell',
331
                'title_row'
332 2
            );
333 2
        }
334 2
        return $this->titleCellPart;
335
    }
336
337
    /**
338
     * @return null|Grid
339
     */
340 2
    protected function getGrid()
341
    {
342 2
        return $this->root;
343
    }
344
}
345