1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace ViewComponents\Grids; |
4
|
|
|
|
5
|
|
|
use Nayjest\Collection\Extended\ObjectCollectionReadInterface; |
6
|
|
|
use RuntimeException; |
7
|
|
|
use ViewComponents\Grids\Component\Column; |
8
|
|
|
use ViewComponents\ViewComponents\Base\ComponentInterface; |
9
|
|
|
use ViewComponents\ViewComponents\Base\Compound\PartInterface; |
10
|
|
|
use ViewComponents\ViewComponents\Component\CollectionView; |
11
|
|
|
use ViewComponents\ViewComponents\Component\Container; |
12
|
|
|
use ViewComponents\ViewComponents\Component\Html\Tag; |
13
|
|
|
use ViewComponents\ViewComponents\Component\ManagedList; |
14
|
|
|
use ViewComponents\ViewComponents\Component\ManagedList\RecordView; |
15
|
|
|
use ViewComponents\ViewComponents\Component\Part; |
16
|
|
|
use ViewComponents\Grids\Component\SolidRow; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Grid component. |
20
|
|
|
*/ |
21
|
|
|
class Grid extends ManagedList |
22
|
|
|
{ |
23
|
|
|
const TABLE_ID = 'table'; |
24
|
|
|
const TABLE_HEADING_ID = 'table_heading'; |
25
|
|
|
const TABLE_FOOTER_ID = 'table_footer'; |
26
|
|
|
const TABLE_BODY_ID = 'table_body'; |
27
|
|
|
const TITLE_ROW_ID = 'title_row'; |
28
|
|
|
const CONTROL_ROW_ID = 'control_row'; |
29
|
|
|
|
30
|
|
|
use GridPartsAccessTrait; |
31
|
|
|
|
32
|
|
|
/** @var mixed|null */ |
33
|
|
|
protected $currentRow; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Returns column collection (readonly). |
37
|
|
|
* |
38
|
|
|
* @return Column[]|ObjectCollectionReadInterface |
39
|
|
|
*/ |
40
|
|
|
public function getColumns() |
41
|
|
|
{ |
42
|
|
|
return $this->getComponents()->filterByType(Column::class); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Returns column with specified id, |
47
|
|
|
* throws exception if grid does not have specified column. |
48
|
|
|
* |
49
|
|
|
* @param string $id |
50
|
|
|
* @return Column |
51
|
|
|
*/ |
52
|
|
|
public function getColumn($id) |
53
|
|
|
{ |
54
|
|
|
$column = $this->getComponent($id); |
55
|
|
|
if (!$column) { |
56
|
|
|
throw new RuntimeException("Column '$id' is not defined."); |
57
|
|
|
} |
58
|
|
|
if (!$column instanceof Column) { |
59
|
|
|
throw new RuntimeException("'$id' is not a column."); |
60
|
|
|
} |
61
|
|
|
return $column; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
public function setControlContainer(ComponentInterface $component) |
65
|
|
|
{ |
66
|
|
|
return $this->setComponent($component, static::CONTROL_CONTAINER_ID, static::CONTROL_ROW_ID); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
public function setSubmitButton(ComponentInterface $component) |
70
|
|
|
{ |
71
|
|
|
return $this->setComponent($component, static::SUBMIT_BUTTON_ID, static::CONTROL_ROW_ID); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function setListContainer(ComponentInterface $component) |
75
|
|
|
{ |
76
|
|
|
return $this->setComponent($component, static::LIST_CONTAINER_ID, static::TABLE_BODY_ID); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* Returns current grid row, used internally for grid rendering. |
81
|
|
|
* |
82
|
|
|
* @internal |
83
|
|
|
* @return mixed |
84
|
|
|
*/ |
85
|
|
|
public function getCurrentRow() |
86
|
|
|
{ |
87
|
|
|
return $this->currentRow; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Sets current grid row for rendering, used internally for grid rendering. |
92
|
|
|
* |
93
|
|
|
* @internal |
94
|
|
|
* @param mixed $currentRow |
95
|
|
|
* @return $this |
96
|
|
|
*/ |
97
|
|
|
public function setCurrentRow($currentRow) |
98
|
|
|
{ |
99
|
|
|
$this->currentRow = $currentRow; |
100
|
|
|
return $this; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
protected function makeDataInjector() |
104
|
|
|
{ |
105
|
|
|
return [$this, 'setCurrentRow']; |
|
|
|
|
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
protected function makeDefaultComponents() |
109
|
|
|
{ |
110
|
|
|
return [ |
111
|
|
|
new Part(new Tag('div'), static::CONTAINER_ID, static::ROOT_ID), |
112
|
|
|
new Part(new Tag('form'), static::FORM_ID, static::CONTAINER_ID), |
113
|
|
|
new Part(new Tag('table'), static::TABLE_ID, static::FORM_ID), // new |
114
|
|
|
new Part(new Tag('thead'), static::TABLE_HEADING_ID, static::TABLE_ID), // new |
115
|
|
|
new Part(new Tag('tr'), static::TITLE_ROW_ID, static::TABLE_HEADING_ID), // new |
116
|
|
|
new Part(new SolidRow(), static::CONTROL_ROW_ID, static::TABLE_HEADING_ID), // new |
117
|
|
|
new Part(new Tag('span'), static::CONTROL_CONTAINER_ID, static::CONTROL_ROW_ID), |
118
|
|
|
new Part(new Tag('input', ['type' => 'submit']), static::SUBMIT_BUTTON_ID, static::CONTROL_ROW_ID), |
119
|
|
|
new Part(new Tag('tbody'), static::TABLE_BODY_ID, static::TABLE_ID), // new |
120
|
|
|
new Part(new Container(), static::LIST_CONTAINER_ID, static::TABLE_BODY_ID), |
121
|
|
|
new Part(new CollectionView(), static::COLLECTION_VIEW_ID, static::LIST_CONTAINER_ID), |
122
|
|
|
new RecordView(new Tag('tr')), |
123
|
|
|
new Part(new Tag('tfoot'), static::TABLE_FOOTER_ID, static::TABLE_ID), // new |
124
|
|
|
]; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Prepares component for rendering. |
129
|
|
|
*/ |
130
|
|
|
protected function prepare() |
131
|
|
|
{ |
132
|
|
|
parent::prepare(); |
133
|
|
|
$this->hideControlRowIfEmpty(); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
protected function hideControlRowIfEmpty() |
137
|
|
|
{ |
138
|
|
|
$row = $this->getControlRow(); |
139
|
|
|
$container = $this->getControlContainer(); |
140
|
|
|
if ( |
141
|
|
|
!$row |
142
|
|
|
|| !$container |
143
|
|
|
|| !$container->children()->isEmpty() |
144
|
|
|
|| ($row->children()->count() > 2) // submit button + control_container |
145
|
|
|
) { |
146
|
|
|
return; |
147
|
|
|
} |
148
|
|
|
/** @var Part $rowPart */ |
149
|
|
|
$rowPart = $this->getComponent(static::CONTROL_ROW_ID, false); |
150
|
|
|
$rowPart->setView(null); |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
|
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.