| Total Complexity | 43 | 
| Total Lines | 311 | 
| Duplicated Lines | 0 % | 
| Changes | 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 | * Initializes the widget.  | 
            ||
| 133 | * @return void  | 
            ||
| 134 | */  | 
            ||
| 135 | public function init()  | 
            ||
| 136 |     { | 
            ||
| 137 | parent::init();  | 
            ||
| 138 | |||
| 139 | $contentData = $this->content;  | 
            ||
| 140 | $cardContentOptions = isset($this->content['options']) ? $this->content['options'] : [];  | 
            ||
| 141 | Html::addCssClass($cardContentOptions, ['class' => 'card-content']);  | 
            ||
| 142 | $this->setCardClass(); // set main css classes to card tag  | 
            ||
| 143 | |||
| 144 |         $html = Html::beginTag('div', $this->options); | 
            ||
| 145 | $html .= $this->renderImageContent();  | 
            ||
| 146 | |||
| 147 | // Create stacked content, if horizontal attribute is true  | 
            ||
| 148 |         if ($this->horizontal) { | 
            ||
| 149 |             $html .= Html::beginTag('div', ['class' => 'card-stacked']); | 
            ||
| 150 | }  | 
            ||
| 151 | |||
| 152 | // Add reviel button to content title, if reveal attribute is not empty  | 
            ||
| 153 |         if (!empty($this->reveal)) { | 
            ||
| 154 | Html::addCssClass($contentData['titleOptions'], 'activator');  | 
            ||
| 155 | $addIcon = !isset($contentData['titleOptions']['icon']);  | 
            ||
| 156 | $contentData['titleOptions']['icon'] = $addIcon  | 
            ||
| 157 | ? 'more_vert'  | 
            ||
| 158 | : $contentData['titleOptions']['icon'];  | 
            ||
| 159 | }  | 
            ||
| 160 | |||
| 161 | // Create card content, if panel not is true  | 
            ||
| 162 |         if (!$this->panel) { | 
            ||
| 163 |             $html .= Html::beginTag('div', $cardContentOptions); | 
            ||
| 164 | }  | 
            ||
| 165 | |||
| 166 | $html .= $this->renderTitleContent($contentData);  | 
            ||
| 167 | |||
| 168 | echo $html;  | 
            ||
| 169 | }  | 
            ||
| 170 | |||
| 171 | /**  | 
            ||
| 172 | * Renders the widget.  | 
            ||
| 173 | * @return void  | 
            ||
| 174 | */  | 
            ||
| 175 | public function run()  | 
            ||
| 176 |     { | 
            ||
| 177 |         $this->registerPlugin('card'); | 
            ||
| 178 | |||
| 179 | $html = isset($this->content['value']) ? $this->content['value'] : '';  | 
            ||
| 180 | |||
| 181 |         if (!$this->panel) { | 
            ||
| 182 |             $html .= Html::endTag('div'); // ends card-content tag | 
            ||
| 183 | }  | 
            ||
| 184 | |||
| 185 |         if (!empty($this->actions)) { | 
            ||
| 186 | Html::addCssClass($this->actionOptions, ['class' => 'card-action']);  | 
            ||
| 187 |             $html .= Html::beginTag('div', $this->actionOptions); | 
            ||
| 188 | |||
| 189 |             foreach ($this->actions as $action) { | 
            ||
| 190 | $html .= $this->renderActionItem($action);  | 
            ||
| 191 | }  | 
            ||
| 192 | |||
| 193 |             $html .= Html::endTag('div'); | 
            ||
| 194 | }  | 
            ||
| 195 | |||
| 196 |         if ($this->horizontal) { | 
            ||
| 197 |             $html .= Html::endTag('div'); //ends card-stacked tag | 
            ||
| 198 | }  | 
            ||
| 199 | |||
| 200 | // Add card reveal tag  | 
            ||
| 201 | $html .= $this->renderRevealContent();  | 
            ||
| 202 |         $html .= Html::endTag('div'); //ends card tag | 
            ||
| 203 | |||
| 204 | echo $html;  | 
            ||
| 205 | }  | 
            ||
| 206 | |||
| 207 | /**  | 
            ||
| 208 | * Sets card CSS class (or several classes) to the card options.  | 
            ||
| 209 | * @return void  | 
            ||
| 210 | */  | 
            ||
| 211 | protected function setCardClass()  | 
            ||
| 212 |     { | 
            ||
| 213 | $useStickyActions = isset($this->actionOptions['sticky']) ? $this->actionOptions['sticky'] : false;  | 
            ||
| 214 | |||
| 215 |         if ($this->panel) { | 
            ||
| 216 | Html::addCssClass($this->options, ['card-panel']);  | 
            ||
| 217 |         } elseif ($this->horizontal) { | 
            ||
| 218 | Html::addCssClass($this->options, ['card', 'horizontal']);  | 
            ||
| 219 |         } elseif ($useStickyActions) { | 
            ||
| 220 | Html::addCssClass($this->options, ['card', 'sticky-action']);  | 
            ||
| 221 | unset($this->actionOptions['sticky']);  | 
            ||
| 222 |         } else { | 
            ||
| 223 | Html::addCssClass($this->options, ['card']);  | 
            ||
| 224 | }  | 
            ||
| 225 | }  | 
            ||
| 226 | |||
| 227 | /**  | 
            ||
| 228 | * Renders card content title tag content.  | 
            ||
| 229 | *  | 
            ||
| 230 | * @param array $source data source for title and options  | 
            ||
| 231 | * @return string the rendering result  | 
            ||
| 232 | */  | 
            ||
| 233 | protected function renderTitleContent($source)  | 
            ||
| 234 |     { | 
            ||
| 235 | $html = '';  | 
            ||
| 236 | |||
| 237 |         if (isset($source['title'])) { | 
            ||
| 238 | $titleValue = $source['title'];  | 
            ||
| 239 | $titleOptions = isset($source['titleOptions']) ? $source['titleOptions'] : [];  | 
            ||
| 240 | $encode = isset($titleOptions['encode']) ? $titleOptions['encode'] : $this->encodeLabels;  | 
            ||
| 241 | $icon = isset($titleOptions['icon']) ? $titleOptions['icon'] : null;  | 
            ||
| 242 | unset($titleOptions['encode']);  | 
            ||
| 243 | Html::addCssClass($titleOptions, 'card-title');  | 
            ||
| 244 | $title = $encode ? Html::encode($titleValue) : $titleValue;  | 
            ||
| 245 | $title .= !empty($icon) ? Html::icon($icon, ['class' => 'material-icons right']) : '';  | 
            ||
| 246 | unset($titleOptions['icon']);  | 
            ||
| 247 |             $html .= Html::tag('span', $title, $titleOptions); | 
            ||
| 248 | }  | 
            ||
| 249 | |||
| 250 | return $html;  | 
            ||
| 251 | }  | 
            ||
| 252 | |||
| 253 | /**  | 
            ||
| 254 | * Renders a single card action item.  | 
            ||
| 255 | *  | 
            ||
| 256 | * @param array $link the link to be rendered. It must contain the "label" element. The "url" and "icon" element is optional.  | 
            ||
| 257 | * @return string the rendering result  | 
            ||
| 258 | * @throws InvalidConfigException if `$link` does not have "label" element.  | 
            ||
| 259 | */  | 
            ||
| 260 | protected function renderActionItem($link)  | 
            ||
| 285 | }  | 
            ||
| 286 | }  | 
            ||
| 287 | |||
| 288 | /**  | 
            ||
| 289 | * Renders card-image tag content.  | 
            ||
| 290 | * @return string the rendering result  | 
            ||
| 291 | */  | 
            ||
| 292 | protected function renderImageContent()  | 
            ||
| 293 |     { | 
            ||
| 294 | $html = '';  | 
            ||
| 295 | |||
| 296 |         if (!empty($this->image)) { | 
            ||
| 297 | $contentData = $this->image;  | 
            ||
| 298 | $fabData = isset($contentData['fab']) ? $contentData['fab'] : [];  | 
            ||
| 299 | $options = isset($contentData['options']) ? $contentData['options'] : [];  | 
            ||
| 300 | $imageOptions = isset($contentData['imageOptions']) ? $contentData['imageOptions'] : [];  | 
            ||
| 301 | $imageUrl = isset($contentData['url']) ? $contentData['url'] : [];  | 
            ||
| 302 | Html::addCssClass($options, ['class' => 'card-image']);  | 
            ||
| 303 | |||
| 304 |             $html .= Html::beginTag('div', $options); | 
            ||
| 305 | $html .= Html::img($imageUrl, $imageOptions);  | 
            ||
| 306 | $html .= $this->renderTitleContent($contentData);  | 
            ||
| 307 | $html .= $this->renderActionButton($fabData);  | 
            ||
| 308 |             $html .= Html::endTag('div'); | 
            ||
| 309 | }  | 
            ||
| 310 | |||
| 311 | return $html;  | 
            ||
| 312 | }  | 
            ||
| 313 | |||
| 314 | /**  | 
            ||
| 315 | * Renders floating action button tag.  | 
            ||
| 316 | *  | 
            ||
| 317 | * @param array $config attribute values and options for Button widget.  | 
            ||
| 318 | * @return string the rendering result  | 
            ||
| 319 | */  | 
            ||
| 320 | protected function renderActionButton($config)  | 
            ||
| 335 | }  | 
            ||
| 336 | |||
| 337 | /**  | 
            ||
| 338 | * Renders card-reveal tag content.  | 
            ||
| 339 | * @return string the rendering result  | 
            ||
| 340 | */  | 
            ||
| 341 | protected function renderRevealContent()  | 
            ||
| 361 | }  | 
            ||
| 362 | }  | 
            ||
| 363 |