Completed
Pull Request — master (#421)
by Robbie
02:06
created

GridFieldAddByDBField   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 10
dl 0
loc 195
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getActions() 0 6 1
B handleAction() 0 55 5
A getDataObjectField() 0 4 1
A setDataObjectField() 0 4 1
A getHTMLFragments() 0 56 2
1
<?php
2
3
namespace SilverStripe\Blog\Forms\GridField;
4
5
use UnexpectedValueException;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\Forms\GridField\GridField;
9
use SilverStripe\Forms\GridField\GridField_ActionProvider;
10
use SilverStripe\Forms\GridField\GridField_FormAction;
11
use SilverStripe\Forms\GridField\GridField_HTMLProvider;
12
use SilverStripe\Forms\TextField;
13
use SilverStripe\ORM\ArrayList;
14
use SilverStripe\Security\Security;
15
use SilverStripe\View\ArrayData;
16
use SilverStripe\View\Requirements;
17
18
/**
19
 * Adds a component which allows a user to add a new DataObject by database field.
20
 *
21
 * @package silverstripe
22
 * @subpackage blog
23
 */
24
class GridFieldAddByDBField implements GridField_ActionProvider, GridField_HTMLProvider
25
{
26
    /**
27
     * HTML Fragment to render the field.
28
     *
29
     * @var string
30
     */
31
    protected $targetFragment;
32
33
    /**
34
     * Default field to create the DataObject by should be Title.
35
     *
36
     * @var string
37
     */
38
    protected $dataObjectField = 'Title';
39
40
    /**
41
     * Creates a text field and add button which allows the user to directly create a new
42
     * DataObject by just entering the title.
43
     *
44
     * @param string $targetFragment
45
     * @param string $dataObjectField
46
     */
47
    public function __construct($targetFragment = 'before', $dataObjectField = 'Title')
48
    {
49
        $this->targetFragment = $targetFragment;
50
        $this->dataObjectField = (string) $dataObjectField;
51
    }
52
53
    /**
54
     * Provide actions to this component.
55
     *
56
     * @param GridField $gridField
57
     *
58
     * @return array
59
     */
60
    public function getActions($gridField)
61
    {
62
        return array(
63
            'add',
64
        );
65
    }
66
67
    /**
68
     * Handles the add action for the given DataObject.
69
     *
70
     * @param $gridField GridField
71
     * @param $actionName string
72
     * @param $arguments mixed
73
     * @param $data array
74
     *
75
     * @return null|SS_HTTPResponse
76
     *
77
     * @throws UnexpectedValueException
78
     */
79
    public function handleAction(GridField $gridField, $actionName, $arguments, $data)
80
    {
81
        if ($actionName == 'add') {
82
            $dbField = $this->getDataObjectField();
83
84
            $objClass = $gridField->getModelClass();
85
86
            /**
87
             * @var DataObject $obj
88
             */
89
            $obj = new $objClass();
90
91
            if ($obj->hasField($dbField)) {
92
                $obj->setCastedField($dbField, $data['gridfieldaddbydbfield'][$obj->ClassName][$dbField]);
93
94
                if ($obj->canCreate()) {
95
                    $id = $gridField->getList()->add($obj);
96
                    if (!$id) {
97
                        $gridField->setCustomValidationMessage(
98
                            _t(
99
                                'GridFieldAddByDBField.AddFail',
100
                                'Unable to save {class} to the database.',
101
                                'Unable to add the DataObject.',
102
                                array(
103
                                    'class' => get_class($obj),
104
                                )
105
                            )
106
                        );
107
                    }
108
                } else {
109
                    return Security::permissionFailure(
110
                        Controller::curr(),
111
                        _t(
112
                            'GridFieldAddByDBField.PermissionFail',
113
                            'You don\'t have permission to create a {class}.',
114
                            'Unable to add the DataObject.',
115
                            array(
116
                                'class' => get_class($obj)
117
                            )
118
                        )
119
                    );
120
                }
121
            } else {
122
                throw new UnexpectedValueException(
123
                    sprintf(
124
                        'Invalid field (%s) on %s.',
125
                        $dbField,
126
                        $obj->ClassName
127
                    )
128
                );
129
            }
130
        }
131
132
        return null;
133
    }
134
135
    /**
136
     * Returns the database field for which we'll add the new data object.
137
     *
138
     * @return string
139
     */
140
    public function getDataObjectField()
141
    {
142
        return $this->dataObjectField;
143
    }
144
145
    /**
146
     * Set the database field.
147
     *
148
     * @param $field string
149
     */
150
    public function setDataObjectField($field)
151
    {
152
        $this->dataObjectField = (string) $field;
153
    }
154
155
    /**
156
     * Renders the TextField and add button to the GridField.
157
     *
158
     * @param $gridField GridField
159
     *
160
     * @return string
161
     */
162
    public function getHTMLFragments($gridField)
163
    {
164
        Requirements::javascript(BLOGGER_DIR . '/js/gridfieldaddbydbfield.js');
165
166
        /**
167
         * @var DataList $dataList
168
         */
169
        $dataList = $gridField->getList();
170
171
        $dataClass = $dataList->dataClass();
172
173
        $obj = singleton($dataClass);
174
175
        if (!$obj->canCreate()) {
176
            return '';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return ''; (string) is incompatible with the return type declared by the interface SilverStripe\Forms\GridF...vider::getHTMLFragments of type array.

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...
177
        }
178
179
        $dbField = $this->getDataObjectField();
180
181
        $textField = TextField::create(
182
            sprintf(
183
                "gridfieldaddbydbfield[%s][%s]",
184
                $obj->ClassName,
185
                Convert::raw2htmlatt($dbField)
186
            )
187
        )
188
            ->setAttribute('placeholder', $obj->fieldLabel($dbField))
189
            ->addExtraClass('no-change-track');
190
191
        $addAction = new GridField_FormAction(
192
            $gridField,
193
            'add',
194
            _t(
195
                'GridFieldAddByDBField.Add',
196
                'Add {name}',
197
                'Add button text',
198
                ['name' => $obj->i18n_singular_name()]
199
            ),
200
            'add',
201
            'add'
0 ignored issues
show
Documentation introduced by
'add' is of type string, but the function expects a array.

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...
202
        );
203
        $addAction->setAttribute('data-icon', 'add');
204
        $addAction->addExtraClass('btn btn-primary');
205
206
        $forTemplate = new ArrayData(array());
207
208
        $forTemplate->Fields = new ArrayList();
209
        $forTemplate->Fields->push($textField);
210
        $forTemplate->Fields->push($addAction);
211
212
        return array(
213
            $this->targetFragment => $forTemplate->renderWith(
214
                'SilverStripe\\Blog\\Forms\\GridField\\GridFieldAddByDBField'
215
            )
216
        );
217
    }
218
}
219