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

src/Component/Column.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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);
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
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);
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);
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(),
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(),
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