| Total Complexity | 48 |
| Total Lines | 343 |
| Duplicated Lines | 0 % |
| Changes | 12 | ||
| 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() |
||
| 393 | } |
||
| 394 | } |
||
| 395 |