Completed
Push — master ( e088fc...e52644 )
by Adam
02:49
created

Control::render()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 36
Code Lines 15

Duplication

Lines 8
Ratio 22.22 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
dl 8
loc 36
ccs 0
cts 14
cp 0
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 15
nc 33
nop 0
crap 72
1
<?php
2
/**
3
 * Control.php
4
 *
5
 * @copyright      Vice v copyright.php
6
 * @license        http://www.ipublikuj.eu
7
 * @author         Adam Kadlec http://www.ipublikuj.eu
8
 * @package        iPublikuj:Widgets!
9
 * @subpackage     Components
10
 * @since          1.0.0
11
 *
12
 * @date           24.07.13
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\Widgets\Components;
18
19
use Nette;
20
use Nette\Application;
21
use Nette\ComponentModel;
22
use Nette\Localization;
23
use Nette\Utils;
24
25
use IPub;
26
use IPub\Widgets\Decorators;
27
use IPub\Widgets\Entities;
28
use IPub\Widgets\Exceptions;
29
use IPub\Widgets\Managers;
30
use IPub\Widgets\Widgets;
31
use Tracy\Debugger;
32
33
/**
34
 * Widgets container control definition
35
 *
36
 * @package        iPublikuj:Widgets!
37
 * @subpackage     Components
38
 *                 
39
 * @author         Adam Kadlec <[email protected]>
40
 *
41
 * @method onAttached(Application\UI\Control $component)
42
 *
43
 * @property Application\UI\ITemplate $template
44
 */
45 1
class Control extends IPub\Widgets\Application\UI\BaseControl
46
{
47
	/**
48
	 * @var array
49
	 */
50
	public $onAttached = [];
51
52
	/**
53
	 * @var Managers\WidgetsManager
54
	 */
55
	protected $widgetsManager;
56
57
	/**
58
	 * @var Managers\DecoratorsManager
59
	 */
60
	protected $decoratorsManager;
61
62
	/**
63
	 * @var Managers\FiltersManager
64
	 */
65
	protected $filtersManager;
66
67
	/**
68
	 * @var string
69
	 */
70
	protected $position;
71
72
	/**
73
	 * @var Decorators\IFactory
74
	 */
75
	protected $decorator;
76
77
	/**
78
	 * @var string
79
	 */
80
	protected $group = 'default';
81
82
	/**
83
	 * @param string $position
84
	 * @param Managers\WidgetsManager $widgetsManager
85
	 * @param Managers\DecoratorsManager $decoratorsManager
86
	 * @param Managers\FiltersManager $filtersManager
87
	 * @param ComponentModel\IContainer $parent
88
	 * @param string|NULL $name
89
	 */
90
	public function __construct(
91
		string $position = 'default',
92
		Managers\WidgetsManager $widgetsManager,
93
		Managers\DecoratorsManager $decoratorsManager,
94
		Managers\FiltersManager $filtersManager,
95
		ComponentModel\IContainer $parent = NULL,
96
		string $name = NULL
97
	) {
98
		parent::__construct($parent, $name);
99
100
		// Store info about widgets position
101
		$this->position = $position;
102
103
		// Extension managers
104
		$this->widgetsManager = $widgetsManager;
105
		$this->decoratorsManager = $decoratorsManager;
106
		$this->filtersManager = $filtersManager;
107
108
		// Register widgets container
109
		$this->addComponent(new ComponentModel\Container(), 'widgets');
110
	}
111
112
	/**
113
	 * Attach component to presenter
114
	 *
115
	 * @param Application\UI\Presenter $presenter
116
	 *
117
	 * @throws Exceptions\DecoratorNotRegisteredException
118
	 */
119
	protected function attached($presenter)
120
	{
121
		parent::attached($presenter);
122
123
		if (!$presenter instanceof Application\UI\Presenter) {
124
			return;
125
		}
126
127
		// Register default raw widget decorator
128
		$this->setDecorator('widgets.decorator.raw');
129
130
		// Call attached event
131
		$this->onAttached($this);
132
	}
133
134
	/**
135
	 * Render widgets in selected position
136
	 */
137
	public function render()
138
	{
139
		foreach (func_get_args() as $arg) {
140
			// Check if decorator name is provided
141
			if (is_string($arg)) {
142
				$this->setDecorator($arg);
143
			}
144
		}
145
146
		// Check if control has template
147
		if ($this->template instanceof Nette\Bridges\ApplicationLatte\Template) {
148
			// Assign vars to template
149
			$this->template->add('widgets', $this->getWidgets());
150
151
			// Check if translator is available
152
			if ($this->getTranslator() instanceof Localization\ITranslator) {
153
				$this->template->setTranslator($this->getTranslator());
154
			}
155
156
			// If template was not defined before...
157 View Code Duplication
			if ($this->template->getFile() === NULL) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
				// Get component actual dir
159
				$dir = dirname($this->getReflection()->getFileName());
160
161
				// ...try to get base component template file
162
				$templateFile = $this->templateFile !== NULL && is_file($this->templateFile) ? $this->templateFile : $dir . DIRECTORY_SEPARATOR . 'template' . DIRECTORY_SEPARATOR . 'default.latte';
163
				$this->template->setFile($templateFile);
164
			}
165
166
			// Render component template
167
			$this->template->render();
168
169
		} else {
170
			throw new Exceptions\InvalidStateException('Widgets container control is without template.');
171
		}
172
	}
173
174
	/**
175
	 * Set widgets outer decorator
176
	 *
177
	 * @param string $decorator
178
	 *
179
	 * @throws Exceptions\DecoratorNotRegisteredException
180
	 */
181
	public function setDecorator(string $decorator)
182
	{
183
		// Try to find decorator factory
184
		if ($factory = $this->decoratorsManager->get($decorator)) {
185
			// Check if some decorator is registered...
186
			if ($component = $this->getComponent('decorator', FALSE)) {
187
				// ... if yes, remove it
188
				$this->removeComponent($component);
189
			}
190
191
			// Register decorator component
192
			$this->addComponent($factory->create(), 'decorator');
0 ignored issues
show
Bug introduced by
The method create() does not seem to exist on object<IPub\Widgets\Decorators\IFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
193
194
		} else {
195
			throw new Exceptions\DecoratorNotRegisteredException(sprintf('Widgets decorator: "%s" is not registered.', $decorator));
196
		}
197
	}
198
199
	/**
200
	 * Set widgets group
201
	 *
202
	 * @param string $group
203
	 */
204
	public function setGroup(string $group)
205
	{
206
		$this->group = $group;
207
	}
208
209
	/**
210
	 * Get widgets group
211
	 *
212
	 * @return string
213
	 */
214
	public function getGroup() : string
215
	{
216
		return $this->group;
217
	}
218
219
	/**
220
	 * Get all registered widgets in position
221
	 *
222
	 * @return array
223
	 */
224
	public function getWidgets()
225
	{
226
		if (
227
			($container = $this->getComponent('widgets')->getComponent($this->group, FALSE))
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: IPub\Widgets\Application\UI\BaseControl, IPub\Widgets\Components\Control, IPub\Widgets\Decorators\Decorator, IPub\Widgets\Decorators\Raw\Control, IPub\Widgets\Widgets\Widget, Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\ComponentModel\Container.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
228
			&& ($positionContainer = $container->getComponent($this->position, FALSE))
229
			&& ($widgets = $positionContainer->getComponents())
230
		) {
231
			// Apply widgets filters
232
			foreach ($this->filtersManager as $filter) {
233
					$widgets = $filter->create($widgets, [
234
						'access' => TRUE,
235
						'active' => TRUE,
236
						'status' => 1,
237
					]);
238
			}
239
240
			return $widgets;
241
		}
242
243
		return [];
244
	}
245
246
	/**
247
	 * Add widget to container
248
	 *
249
	 * @param string $name
250
	 * @param array $data
251
	 * @param string|NULL $group
252
	 * @param string|NULL $position
253
	 *
254
	 * @throws Exceptions\WidgetNotRegisteredException
255
	 * @throws Exceptions\InvalidStateException
256
	 */
257
	public function addWidget(string $name, array $data = [], string $group = NULL, string $position = NULL)
258
	{
259
		if ($position === NULL) {
260
			$position = $this->position;
261
		}
262
263
		if ($group === NULL) {
264
			$group = $this->group;
265
		}
266
267
		// Prepare widget settings data
268
		$data = $this->createData($data);
269
270
		if (!$factory = $this->widgetsManager->get($name, $group)) {
271
			throw new Exceptions\WidgetNotRegisteredException(sprintf('Widget of type "%s" in group "%s" is not registered.', $name, $group));
272
		}
273
274
		// Check container exist
275
		$container = $this->getComponent('widgets')->getComponent($group, FALSE);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: IPub\Widgets\Application\UI\BaseControl, IPub\Widgets\Components\Control, IPub\Widgets\Decorators\Decorator, IPub\Widgets\Decorators\Raw\Control, IPub\Widgets\Widgets\Widget, Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\ComponentModel\Container.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
276
277
		if (!$container) {
278
			$this->getComponent('widgets')->addComponent(new Nette\ComponentModel\Container, $group);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method addComponent() does only exist in the following implementations of said interface: IPub\Widgets\Application\UI\BaseControl, IPub\Widgets\Components\Control, IPub\Widgets\Decorators\Decorator, IPub\Widgets\Decorators\Raw\Control, IPub\Widgets\Widgets\Widget, Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\ComponentModel\Container.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
279
			$container = $this->getComponent('widgets')->getComponent($group);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Nette\ComponentModel\IComponent as the method getComponent() does only exist in the following implementations of said interface: IPub\Widgets\Application\UI\BaseControl, IPub\Widgets\Components\Control, IPub\Widgets\Decorators\Decorator, IPub\Widgets\Decorators\Raw\Control, IPub\Widgets\Widgets\Widget, Nette\Application\UI\Component, Nette\Application\UI\Control, Nette\Application\UI\Multiplier, Nette\Application\UI\Presenter, Nette\ComponentModel\Container.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
280
		}
281
282
		// Check container exist
283
		$positionContainer = $container->getComponent($position, FALSE);
284
285
		if (!$positionContainer) {
286
			$container->addComponent(new Nette\ComponentModel\Container, $position);
287
			$positionContainer = $container->getComponent($position);
288
		}
289
290
		// Create component
291
		$widget = $factory->create($data);
0 ignored issues
show
Bug introduced by
The method create() does not seem to exist on object<IPub\Widgets\Widgets\IFactory>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
292
293
		// Add widget component to container/position
294
		$positionContainer->addComponent($widget, ($widget->getName() . spl_object_hash($data)));
295
	}
296
297
	/**
298
	 * Convert data to object
299
	 *
300
	 * @param mixed $data
301
	 *
302
	 * @return Entities\IData
303
	 *
304
	 * @throws Exceptions\InvalidStateException
305
	 */
306
	private function createData($data) : Entities\IData
307
	{
308
		// Data are in required object
309
		if ($data instanceof Entities\IData) {
310
			return $data;
311
312
		// or data are in array
313
		} elseif (is_array($data)) {
314
			// Create new data object
315
			return (new Entities\Data($data));
316
317
		// or data are in ArrayHash object
318
		} elseif ($data instanceof Utils\ArrayHash) {
319
			// Create new data object
320
			return (new Entities\Data((array) $data));
321
		}
322
323
		throw new Exceptions\InvalidStateException('Widget data could not be converted to data entity.');
324
	}
325
326
	/**
327
	 * @param string $name
328
	 * @param mixed $args
329
	 *
330
	 * @return mixed
331
	 *
332
	 * @throws Exceptions\DecoratorNotRegisteredException
333
	 */
334
	public function __call($name, $args)
335
	{
336
		if (Utils\Strings::startsWith($name, 'render')) {
337
			// Get decorator name
338
			if ($decoratorName = Utils\Strings::capitalize(Utils\Strings::substring($name, 6))) {
339
				// Set widget decorator
340
				$this->setDecorator($decoratorName);
341
			}
342
343
			// Call component rendering
344
			$this->render();
345
346
		} else {
347
			return parent::__call($name, $args);
348
		}
349
	}
350
}
351