Completed
Push — master ( 0636af...e088fc )
by Adam
07:34
created

Control   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 293
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 2.78%

Importance

Changes 0
Metric Value
wmc 31
lcom 2
cbo 11
dl 0
loc 293
ccs 2
cts 72
cp 0.0278
rs 9.8
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 1
A attached() 0 14 2
B render() 0 29 6
A setDecorator() 0 11 2
A setGroup() 0 4 1
A getGroup() 0 4 1
B getWidgets() 0 21 5
B addWidget() 0 39 6
A createData() 0 19 4
A __call() 0 16 3
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
32
/**
33
 * Widgets container control definition
34
 *
35
 * @package        iPublikuj:Widgets!
36
 * @subpackage     Components
37
 *                 
38
 * @author         Adam Kadlec <[email protected]>
39
 *
40
 * @method onAttached(Application\UI\Control $component)
41
 *
42
 * @property Application\UI\ITemplate $template
43
 */
44 1
class Control extends IPub\Widgets\Application\UI\BaseControl
45
{
46
	/**
47
	 * @var array
48
	 */
49
	public $onAttached = [];
50
51
	/**
52
	 * @var Managers\WidgetsManager
53
	 */
54
	protected $widgetsManager;
55
56
	/**
57
	 * @var Managers\DecoratorsManager
58
	 */
59
	protected $decoratorsManager;
60
61
	/**
62
	 * @var Managers\FiltersManager
63
	 */
64
	protected $filtersManager;
65
66
	/**
67
	 * @var string
68
	 */
69
	protected $position;
70
71
	/**
72
	 * @var Decorators\IFactory
73
	 */
74
	protected $decorator;
75
76
	/**
77
	 * @var string
78
	 */
79
	protected $group = 'default';
80
81
	/**
82
	 * @param string $position
83
	 * @param Managers\WidgetsManager $widgetsManager
84
	 * @param Managers\DecoratorsManager $decoratorsManager
85
	 * @param Managers\FiltersManager $filtersManager
86
	 * @param ComponentModel\IContainer $parent
87
	 * @param string|NULL $name
88
	 */
89
	public function __construct(
90
		string $position = 'default',
91
		Managers\WidgetsManager $widgetsManager,
92
		Managers\DecoratorsManager $decoratorsManager,
93
		Managers\FiltersManager $filtersManager,
94
		ComponentModel\IContainer $parent = NULL,
95
		string $name = NULL
96
	) {
97
		parent::__construct($parent, $name);
98
99
		// Store info about widgets position
100
		$this->position = $position;
101
102
		// Extension managers
103
		$this->widgetsManager = $widgetsManager;
104
		$this->decoratorsManager = $decoratorsManager;
105
		$this->filtersManager = $filtersManager;
106
107
		// Register widgets container
108
		$this->addComponent(new ComponentModel\Container(), 'widgets');
109
	}
110
111
	/**
112
	 * Attach component to presenter
113
	 *
114
	 * @param Application\UI\Presenter $presenter
115
	 *
116
	 * @throws Exceptions\DecoratorNotRegisteredException
117
	 */
118
	protected function attached($presenter)
119
	{
120
		parent::attached($presenter);
121
122
		if (!$presenter instanceof Application\UI\Presenter) {
123
			return;
124
		}
125
126
		// Register default raw widget decorator
127
		$this->setDecorator('widgets.decorator.raw');
128
129
		// Call attached event
130
		$this->onAttached($this);
131
	}
132
133
	/**
134
	 * Render widgets in selected position
135
	 */
136
	public function render()
137
	{
138
		// Check if control has template
139
		if ($this->template instanceof Nette\Bridges\ApplicationLatte\Template) {
140
			// Assign vars to template
141
			$this->template->add('widgets', $this->getWidgets());
142
143
			// Check if translator is available
144
			if ($this->getTranslator() instanceof Localization\ITranslator) {
145
				$this->template->setTranslator($this->getTranslator());
146
			}
147
148
			// If template was not defined before...
149
			if ($this->template->getFile() === NULL) {
150
				// Get component actual dir
151
				$dir = dirname($this->getReflection()->getFileName());
152
153
				// ...try to get base component template file
154
				$templateFile = $this->templateFile !== NULL && is_file($this->templateFile) ? $this->templateFile : $dir . DIRECTORY_SEPARATOR . 'template' . DIRECTORY_SEPARATOR . 'default.latte';
155
				$this->template->setFile($templateFile);
156
			}
157
158
			// Render component template
159
			$this->template->render();
160
161
		} else {
162
			throw new Exceptions\InvalidStateException('Widgets container control is without template.');
163
		}
164
	}
165
166
	/**
167
	 * Set widgets outer decorator
168
	 *
169
	 * @param string $decorator
170
	 *
171
	 * @throws Exceptions\DecoratorNotRegisteredException
172
	 */
173
	public function setDecorator(string $decorator)
174
	{
175
		// Try to find decorator factory
176
		if ($factory = $this->decoratorsManager->get($decorator)) {
177
			// Register decorator component
178
			$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...
179
180
		} else {
181
			throw new Exceptions\DecoratorNotRegisteredException(sprintf('Widgets decorator: "%s" is not registered.', $decorator));
182
		}
183
	}
184
185
	/**
186
	 * Set widgets group
187
	 *
188
	 * @param string $group
189
	 */
190
	public function setGroup(string $group)
191
	{
192
		$this->group = $group;
193
	}
194
195
	/**
196
	 * Get widgets group
197
	 *
198
	 * @return string
199
	 */
200
	public function getGroup() : string
201
	{
202
		return $this->group;
203
	}
204
205
	/**
206
	 * Get all registered widgets in position
207
	 *
208
	 * @return array
209
	 */
210
	public function getWidgets()
211
	{
212
		if (
213
			($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...
214
			&& ($positionContainer = $container->getComponent($this->position, FALSE))
215
			&& ($widgets = $positionContainer->getComponents())
216
		) {
217
			// Apply widgets filters
218
			foreach ($this->filtersManager as $filter) {
219
					$widgets = $filter->create($widgets, [
220
						'access' => TRUE,
221
						'active' => TRUE,
222
						'status' => 1,
223
					]);
224
			}
225
226
			return $widgets;
227
		}
228
229
		return [];
230
	}
231
232
	/**
233
	 * Add widget to container
234
	 *
235
	 * @param string $name
236
	 * @param array $data
237
	 * @param string|NULL $group
238
	 * @param string|NULL $position
239
	 *
240
	 * @throws Exceptions\WidgetNotRegisteredException
241
	 * @throws Exceptions\InvalidStateException
242
	 */
243
	public function addWidget(string $name, array $data = [], string $group = NULL, string $position = NULL)
244
	{
245
		if ($position === NULL) {
246
			$position = $this->position;
247
		}
248
249
		if ($group === NULL) {
250
			$group = $this->group;
251
		}
252
253
		// Prepare widget settings data
254
		$data = $this->createData($data);
255
256
		if (!$factory = $this->widgetsManager->get($name, $group)) {
257
			throw new Exceptions\WidgetNotRegisteredException(sprintf('Widget of type "%s" in group "%s" is not registered.', $name, $group));
258
		}
259
260
		// Check container exist
261
		$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...
262
263
		if (!$container) {
264
			$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...
265
			$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...
266
		}
267
268
		// Check container exist
269
		$positionContainer = $container->getComponent($position, FALSE);
270
271
		if (!$positionContainer) {
272
			$container->addComponent(new Nette\ComponentModel\Container, $position);
273
			$positionContainer = $container->getComponent($position);
274
		}
275
276
		// Create component
277
		$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...
278
279
		// Add widget component to container/position
280
		$positionContainer->addComponent($widget, ($widget->getName() . spl_object_hash($data)));
281
	}
282
283
	/**
284
	 * Convert data to object
285
	 *
286
	 * @param mixed $data
287
	 *
288
	 * @return Entities\IData
289
	 *
290
	 * @throws Exceptions\InvalidStateException
291
	 */
292
	private function createData($data) : Entities\IData
293
	{
294
		// Data are in required object
295
		if ($data instanceof Entities\IData) {
296
			return $data;
297
298
		// or data are in array
299
		} elseif (is_array($data)) {
300
			// Create new data object
301
			return (new Entities\Data($data));
302
303
		// or data are in ArrayHash object
304
		} elseif ($data instanceof Utils\ArrayHash) {
305
			// Create new data object
306
			return (new Entities\Data((array) $data));
307
		}
308
309
		throw new Exceptions\InvalidStateException('Widget data could not be converted to data entity.');
310
	}
311
312
	/**
313
	 * @param string $name
314
	 * @param mixed $args
315
	 *
316
	 * @return mixed
317
	 *
318
	 * @throws Exceptions\DecoratorNotRegisteredException
319
	 */
320
	public function __call($name, $args)
321
	{
322
		if (Utils\Strings::startsWith($name, 'render')) {
323
			// Get decorator name
324
			if ($decoratorName = Utils\Strings::capitalize(Utils\Strings::substring($name, 6))) {
325
				// Set widget decorator
326
				$this->setDecorator($decoratorName);
327
			}
328
329
			// Call component rendering
330
			$this->render();
331
332
		} else {
333
			return parent::__call($name, $args);
334
		}
335
	}
336
}
337