| Total Complexity | 48 | 
| Total Lines | 344 | 
| Duplicated Lines | 0 % | 
| Changes | 13 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like Card often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Card, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 50 | class Card extends Widget  | 
            ||
| 51 | { | 
            ||
| 52 | /**  | 
            ||
| 53 | * @var bool whether to HTML-encode the link labels and card title  | 
            ||
| 54 | */  | 
            ||
| 55 | public $encodeLabels = true;  | 
            ||
| 56 | |||
| 57 | /**  | 
            ||
| 58 | * @var array a list of attributes to be displayed in the card content tag. Item should be an array  | 
            ||
| 59 | * of the following structure:  | 
            ||
| 60 | * - title: string, title for the card image. Value will be HTML-encoded.  | 
            ||
| 61 | * You can change this, by setting extra attribute ["encode" => false] in "titleOptions" attribute  | 
            ||
| 62 | * - value: string, the HTML content for the card body. It will NOT be HTML-encoded.  | 
            ||
| 63 | * Therefore you can pass in HTML code. If this is coming from end users,  | 
            ||
| 64 | * you should consider encode() it to prevent XSS attacks.  | 
            ||
| 65 | * - options: array, the HTML attributes for the card content tag.  | 
            ||
| 66 | * - titleOptions: array the HTML attributes for the title tag. You can specify 'icon' attribute  | 
            ||
| 67 | * (name for the icon), if you want to add icon to the right side of the title.  | 
            ||
| 68 | */  | 
            ||
| 69 | public $content = [];  | 
            ||
| 70 | |||
| 71 | /**  | 
            ||
| 72 | * @var array a list of attributes to be displayed in the card image tag. Item should be an array  | 
            ||
| 73 | * of the following structure:  | 
            ||
| 74 | * - title: string, title for the card image. Value will be HTML-encoded.  | 
            ||
| 75 | * You can change this, by setting extra attribute ["encode" => false] in "titleOptions" attribute  | 
            ||
| 76 | * - url: string the image URL. This parameter will be processed by [[Url::to()]].  | 
            ||
| 77 | * - fab: array list of attributes for floating action button. Value will be passed to [[Button]] widget.  | 
            ||
| 78 | * You can set extra param 'url' to render it as link.  | 
            ||
| 79 | * - options: array, the HTML attributes for the card image tag.  | 
            ||
| 80 | * - titleOptions: array the HTML attributes for the title tag. You can specify 'icon' attribute  | 
            ||
| 81 | * (name for the icon), if you want to add icon to the right side of the title.  | 
            ||
| 82 | * - imageOptions: array the HTML attributes for the image tag.  | 
            ||
| 83 | */  | 
            ||
| 84 | public $image = [];  | 
            ||
| 85 | |||
| 86 | /**  | 
            ||
| 87 | * @var array list of card action items. Each action item should be an array of the following structure:  | 
            ||
| 88 | * - label: string, specifies the action item label. When [[encodeLabels]] is true, the label  | 
            ||
| 89 | * will be HTML-encoded.  | 
            ||
| 90 | * - encode: boolean, optional, whether this item`s label should be HTML-encoded. This param will override  | 
            ||
| 91 | * global [[encodeLabels]] param.  | 
            ||
| 92 | * - url: string or array, optional, specifies the URL of the action item. It will be processed by [[Url::to]].  | 
            ||
| 93 | * - icon: string or array, optional, icon name or array with 'name' and 'options'.  | 
            ||
| 94 | * - options: array, optional, the HTML attributes for the action container tag.  | 
            ||
| 95 | */  | 
            ||
| 96 | public $actions = [];  | 
            ||
| 97 | |||
| 98 | /**  | 
            ||
| 99 | * @var array the HTML attributes for the action wrapper tag of the card view.  | 
            ||
| 100 | * You can use attribute "sticky" to change, whether card actions must be always visible.  | 
            ||
| 101 | * Default value is false.  | 
            ||
| 102 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.  | 
            ||
| 103 | */  | 
            ||
| 104 | public $actionOptions = [];  | 
            ||
| 105 | |||
| 106 | /**  | 
            ||
| 107 | * @var array a list of attributes to be displayed in the card reveal tag. Item should be an array  | 
            ||
| 108 | * of the following structure:  | 
            ||
| 109 | * - title: string, title for the card image. Value will be HTML-encoded.  | 
            ||
| 110 | * You can change this, by setting extra attribute ["encode" => false] in "titleOptions" attribute  | 
            ||
| 111 | * - value: string, the HTML content for the card body. It will NOT be HTML-encoded.  | 
            ||
| 112 | * Therefore you can pass in HTML code. If this is coming from end users,  | 
            ||
| 113 | * you should consider encode() it to prevent XSS attacks.  | 
            ||
| 114 | * - options: array, the HTML attributes for the card content tag.  | 
            ||
| 115 | * - titleOptions: array the HTML attributes for the title tag. You can specify 'icon' attribute  | 
            ||
| 116 | * (name for the icon), if you want to add icon to the right side of the title.  | 
            ||
| 117 | */  | 
            ||
| 118 | public $reveal = [];  | 
            ||
| 119 | |||
| 120 | /**  | 
            ||
| 121 | * @var bool whether image should be on left side. Default value is false  | 
            ||
| 122 | */  | 
            ||
| 123 | public $horizontal = false;  | 
            ||
| 124 | |||
| 125 | /**  | 
            ||
| 126 | * @var bool whether card should be spaced as panel. Default value is false.  | 
            ||
| 127 | * Use this for a simpler card with less markup, because it has only padding and a shadow effect.  | 
            ||
| 128 | */  | 
            ||
| 129 | public $panel = false;  | 
            ||
| 130 | |||
| 131 | /**  | 
            ||
| 132 | * @var Tabs the class for tab content creation  | 
            ||
| 133 | * @see \dmgpage\yii2materialize\widgets\Tabs for details on how content are being rendered.  | 
            ||
| 134 | */  | 
            ||
| 135 | public $tabs;  | 
            ||
| 136 | |||
| 137 | /**  | 
            ||
| 138 | * Initializes the widget.  | 
            ||
| 139 | * @return void  | 
            ||
| 140 | */  | 
            ||
| 141 | public function init()  | 
            ||
| 142 |     { | 
            ||
| 143 | parent::init();  | 
            ||
| 144 | |||
| 145 | $contentData = $this->content;  | 
            ||
| 146 | $cardContentOptions = isset($this->content['options']) ? $this->content['options'] : [];  | 
            ||
| 147 | Html::addCssClass($cardContentOptions, ['class' => 'card-content']);  | 
            ||
| 148 | $this->setCardClass(); // set main css classes to card tag  | 
            ||
| 149 | |||
| 150 |         $html = Html::beginTag('div', $this->options); | 
            ||
| 151 | $html .= $this->renderImageContent();  | 
            ||
| 152 | |||
| 153 | // Create stacked content, if horizontal attribute is true  | 
            ||
| 154 |         if ($this->horizontal) { | 
            ||
| 155 |             $html .= Html::beginTag('div', ['class' => 'card-stacked']); | 
            ||
| 156 | }  | 
            ||
| 157 | |||
| 158 | // Add reviel button to content title, if reveal attribute is not empty  | 
            ||
| 159 |         if (!empty($this->reveal)) { | 
            ||
| 160 | Html::addCssClass($contentData['titleOptions'], 'activator');  | 
            ||
| 161 | $addIcon = !isset($contentData['titleOptions']['icon']);  | 
            ||
| 162 | $contentData['titleOptions']['icon'] = $addIcon  | 
            ||
| 163 | ? 'more_vert'  | 
            ||
| 164 | : $contentData['titleOptions']['icon'];  | 
            ||
| 165 | }  | 
            ||
| 166 | |||
| 167 | // Create card content, if panel not true  | 
            ||
| 168 |         if (!$this->panel) { | 
            ||
| 169 |             $html .= Html::beginTag('div', $cardContentOptions); | 
            ||
| 170 | }  | 
            ||
| 171 | |||
| 172 | // Validate tab attribute  | 
            ||
| 173 |         if ($this->tabs && !$this->tabs instanceof Tabs) { | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 174 |             throw new InvalidConfigException('"' . get_class($this) . '::$tabs" should be an instance of "\dmgpage\yii2materialize\widgets\Tabs".'); | 
            ||
| 175 | }  | 
            ||
| 176 | |||
| 177 | $html .= $this->renderTitleContent($contentData);  | 
            ||
| 178 | |||
| 179 | echo $html;  | 
            ||
| 180 | }  | 
            ||
| 181 | |||
| 182 | /**  | 
            ||
| 183 | * Renders the widget.  | 
            ||
| 184 | * @return void  | 
            ||
| 185 | */  | 
            ||
| 186 | public function run()  | 
            ||
| 187 |     { | 
            ||
| 188 |         $this->registerPlugin('card'); | 
            ||
| 189 | |||
| 190 | $html = isset($this->content['value']) ? $this->content['value'] : '';  | 
            ||
| 191 | |||
| 192 |         if (!$this->panel) { | 
            ||
| 193 |             $html .= Html::endTag('div'); // ends card-content tag | 
            ||
| 194 | }  | 
            ||
| 195 | |||
| 196 |         if (!empty($this->actions)) { | 
            ||
| 197 | Html::addCssClass($this->actionOptions, ['class' => 'card-action']);  | 
            ||
| 198 |             $html .= Html::beginTag('div', $this->actionOptions); | 
            ||
| 199 | |||
| 200 |             foreach ($this->actions as $action) { | 
            ||
| 201 | $html .= $this->renderActionItem($action);  | 
            ||
| 202 | }  | 
            ||
| 203 | |||
| 204 |             $html .= Html::endTag('div'); | 
            ||
| 205 | }  | 
            ||
| 206 | |||
| 207 |         if ($this->horizontal) { | 
            ||
| 208 |             $html .= Html::endTag('div'); //ends card-stacked tag | 
            ||
| 209 | }  | 
            ||
| 210 | |||
| 211 |         if ($this->tabs) { | 
            ||
| 212 | $html .= $this->renderTabsItem();  | 
            ||
| 213 | }  | 
            ||
| 214 | |||
| 215 | // Add card reveal tag  | 
            ||
| 216 | $html .= $this->renderRevealContent();  | 
            ||
| 217 |         $html .= Html::endTag('div'); //ends card tag | 
            ||
| 218 | |||
| 219 | echo $html;  | 
            ||
| 220 | }  | 
            ||
| 221 | |||
| 222 | /**  | 
            ||
| 223 | * Sets card CSS class (or several classes) to the card options.  | 
            ||
| 224 | * @return void  | 
            ||
| 225 | */  | 
            ||
| 226 | protected function setCardClass()  | 
            ||
| 227 |     { | 
            ||
| 228 | $useStickyActions = isset($this->actionOptions['sticky']) ? $this->actionOptions['sticky'] : false;  | 
            ||
| 229 | |||
| 230 |         if ($this->panel) { | 
            ||
| 231 | Html::addCssClass($this->options, ['card-panel']);  | 
            ||
| 232 |         } elseif ($this->horizontal) { | 
            ||
| 233 | Html::addCssClass($this->options, ['card', 'horizontal']);  | 
            ||
| 234 |         } elseif ($useStickyActions) { | 
            ||
| 235 | Html::addCssClass($this->options, ['card', 'sticky-action']);  | 
            ||
| 236 | unset($this->actionOptions['sticky']);  | 
            ||
| 237 |         } else { | 
            ||
| 238 | Html::addCssClass($this->options, ['card']);  | 
            ||
| 239 | }  | 
            ||
| 240 | }  | 
            ||
| 241 | |||
| 242 | /**  | 
            ||
| 243 | * Renders card content title tag content.  | 
            ||
| 244 | *  | 
            ||
| 245 | * @param array $source data source for title and options  | 
            ||
| 246 | * @return string the rendering result  | 
            ||
| 247 | */  | 
            ||
| 248 | protected function renderTitleContent($source)  | 
            ||
| 249 |     { | 
            ||
| 250 | $html = '';  | 
            ||
| 251 | |||
| 252 |         if (isset($source['title'])) { | 
            ||
| 253 | $titleValue = $source['title'];  | 
            ||
| 254 | $titleOptions = isset($source['titleOptions']) ? $source['titleOptions'] : [];  | 
            ||
| 255 | $encode = isset($titleOptions['encode']) ? $titleOptions['encode'] : $this->encodeLabels;  | 
            ||
| 256 | $icon = isset($titleOptions['icon']) ? $titleOptions['icon'] : null;  | 
            ||
| 257 | unset($titleOptions['encode']);  | 
            ||
| 258 | Html::addCssClass($titleOptions, 'card-title');  | 
            ||
| 259 | $title = $encode ? Html::encode($titleValue) : $titleValue;  | 
            ||
| 260 | $title .= !empty($icon) ? Html::icon($icon, ['class' => 'material-icons right']) : '';  | 
            ||
| 261 | unset($titleOptions['icon']);  | 
            ||
| 262 |             $html .= Html::tag('span', $title, $titleOptions); | 
            ||
| 263 | }  | 
            ||
| 264 | |||
| 265 | return $html;  | 
            ||
| 266 | }  | 
            ||
| 267 | |||
| 268 | /**  | 
            ||
| 269 | * Renders a single card action item.  | 
            ||
| 270 | *  | 
            ||
| 271 | * @param array $link the link to be rendered. It must contain the "label" element. The "url" and "icon" element is optional.  | 
            ||
| 272 | * @return string the rendering result  | 
            ||
| 273 | * @throws InvalidConfigException if `$link` does not have "label" element.  | 
            ||
| 274 | */  | 
            ||
| 275 | protected function renderActionItem($link)  | 
            ||
| 300 | }  | 
            ||
| 301 | }  | 
            ||
| 302 | |||
| 303 | /**  | 
            ||
| 304 | * Renders card-image tag content.  | 
            ||
| 305 | * @return string the rendering result  | 
            ||
| 306 | */  | 
            ||
| 307 | protected function renderImageContent()  | 
            ||
| 308 |     { | 
            ||
| 309 | $html = '';  | 
            ||
| 310 | |||
| 311 |         if (!empty($this->image)) { | 
            ||
| 312 | $contentData = $this->image;  | 
            ||
| 313 | $fabData = isset($contentData['fab']) ? $contentData['fab'] : [];  | 
            ||
| 314 | $options = isset($contentData['options']) ? $contentData['options'] : [];  | 
            ||
| 315 | $imageOptions = isset($contentData['imageOptions']) ? $contentData['imageOptions'] : [];  | 
            ||
| 316 | $imageUrl = isset($contentData['url']) ? $contentData['url'] : [];  | 
            ||
| 317 | Html::addCssClass($options, ['class' => 'card-image']);  | 
            ||
| 318 | |||
| 319 |             $html .= Html::beginTag('div', $options); | 
            ||
| 320 | $html .= Html::img($imageUrl, $imageOptions);  | 
            ||
| 321 | $html .= $this->renderTitleContent($contentData);  | 
            ||
| 322 | $html .= $this->renderActionButton($fabData);  | 
            ||
| 323 |             $html .= Html::endTag('div'); | 
            ||
| 324 | }  | 
            ||
| 325 | |||
| 326 | return $html;  | 
            ||
| 327 | }  | 
            ||
| 328 | |||
| 329 | /**  | 
            ||
| 330 | * Renders floating action button tag.  | 
            ||
| 331 | *  | 
            ||
| 332 | * @param array $config attribute values and options for Button widget.  | 
            ||
| 333 | * @return string the rendering result  | 
            ||
| 334 | */  | 
            ||
| 335 | protected function renderActionButton($config)  | 
            ||
| 350 | }  | 
            ||
| 351 | |||
| 352 | /**  | 
            ||
| 353 | * Renders card-reveal tag content.  | 
            ||
| 354 | * @return string the rendering result  | 
            ||
| 355 | */  | 
            ||
| 356 | protected function renderRevealContent()  | 
            ||
| 376 | }  | 
            ||
| 377 | |||
| 378 | /**  | 
            ||
| 379 | * Renders card-tabs tag content.  | 
            ||
| 380 | * @return string the rendering result  | 
            ||
| 381 | */  | 
            ||
| 382 | protected function renderTabsItem()  | 
            ||
| 396 |