Completed
Push — master ( c6fc9b...0ece6e )
by Vitaliy
02:48
created

Grid::hideControlRowIfEmpty()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 16
rs 8.8571
cc 5
eloc 11
nc 2
nop 0
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'];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array($this, 'setCurrentRow'); (array<ViewComponents\Grids\Grid|string>) is incompatible with the return type of the parent method ViewComponents\ViewCompo...dList::makeDataInjector of type Closure.

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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