Completed
Push — master ( a57682...eae036 )
by Philip
08:54 queued 04:10
created

ServiceProvider::getEntitiesNavBar()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 9
cts 10
cp 0.9
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 0
crap 3.009
1
<?php
2
3
/*
4
 * This file is part of the CRUDlex package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CRUDlex;
13
14
use League\Flysystem\Adapter\Local;
15
use League\Flysystem\Filesystem;
16
use Pimple\Container;
17
use Pimple\ServiceProviderInterface;
18
use Silex\Api\BootableProviderInterface;
19
use Silex\Application;
20
use Silex\Provider\LocaleServiceProvider;
21
use Silex\Provider\SessionServiceProvider;
22
use Silex\Provider\TranslationServiceProvider;
23
use Silex\Provider\TwigServiceProvider;
24
use Symfony\Component\Translation\Loader\YamlFileLoader;
25
26
/**
27
 * The ServiceProvider setups and initializes the whole CRUD system.
28
 * After adding it to your Silex-setup, it offers access to AbstractData
29
 * instances, one for each defined entity off the CRUD YAML file.
30
 */
31
class ServiceProvider implements ServiceProviderInterface, BootableProviderInterface
32
{
33
34
    /**
35
     * Holds the data instances.
36
     * @var array
37
     */
38
    protected $datas;
39
40
    /**
41
     * Initializes needed but yet missing service providers.
42
     *
43
     * @param Container $app
44
     * the application container
45
     */
46 75
    protected function initMissingServiceProviders(Container $app)
47
    {
48
49 75
        if (!$app->offsetExists('translator')) {
50 75
            $app->register(new LocaleServiceProvider());
51 75
            $app->register(new TranslationServiceProvider(), [
52 75
                'locale_fallbacks' => ['en'],
53 75
            ]);
54 75
        }
55
56 75
        if (!$app->offsetExists('session')) {
57 65
            $app->register(new SessionServiceProvider());
58 65
        }
59
60 75
        if (!$app->offsetExists('twig')) {
61 65
            $app->register(new TwigServiceProvider());
62 65
        }
63 75
        $app['twig.loader.filesystem']->addPath(__DIR__.'/../views/', 'crud');
64 75
    }
65
66
    /**
67
     * Initializes the available locales.
68
     *
69
     * @param Container $app
70
     * the application container
71
     *
72
     * @return array
73
     * the available locales
74
     */
75 74
    protected function initLocales(Container $app)
76
    {
77 74
        $locales   = $this->getLocales();
78 74
        $localeDir = __DIR__.'/../locales';
79 74
        $app['translator']->addLoader('yaml', new YamlFileLoader());
80 74
        foreach ($locales as $locale) {
81 74
            $app['translator']->addResource('yaml', $localeDir.'/'.$locale.'.yml', $locale);
82 74
        }
83 74
        return $locales;
84
    }
85
86
    /**
87
     * Initializes the children of the data entries.
88
     */
89 74
    protected function initChildren()
90
    {
91 74
        foreach ($this->datas as $name => $data) {
92 74
            $fields = $data->getDefinition()->getFieldNames();
93 74
            foreach ($fields as $field) {
94 74
                if ($data->getDefinition()->getType($field) == 'reference') {
95 73
                    $this->datas[$data->getDefinition()->getSubTypeField($field, 'reference', 'entity')]->getDefinition()->addChild($data->getDefinition()->getTable(), $field, $name);
96 73
                }
97 74
            }
98 74
        }
99 74
    }
100
101
    /**
102
     * Gets a map with localized entity labels from the CRUD YML.
103
     *
104
     * @param array $locales
105
     * the available locales
106
     * @param array $crud
107
     * the CRUD entity map
108
     *
109
     * @return array
110
     * the map with localized entity labels
111
     */
112 75
    protected function getLocaleLabels(array $locales, array $crud)
113 75
    {
114 74
        $localeLabels = [];
115 74
        foreach ($locales as $locale) {
116 74
            if (array_key_exists('label_'.$locale, $crud)) {
117 74
                $localeLabels[$locale] = $crud['label_'.$locale];
118 74
            }
119 74
        }
120 74
        return $localeLabels;
121
    }
122
123
    /**
124
     * Configures the EntityDefinition according to the given
125
     * CRUD entity map.
126
     *
127
     * @param EntityDefinition $definition
128
     * the definition to configure
129
     * @param array $crud
130
     * the CRUD entity map
131
     */
132 74
    protected function configureDefinition(EntityDefinition $definition, array $crud)
133
    {
134
        $toConfigure = [
135 74
            'deleteCascade',
136 74
            'listFields',
137 74
            'filter',
138 74
            'childrenLabelFields',
139 74
            'pageSize',
140 74
            'initialSortField',
141 74
            'initialSortAscending',
142 74
            'navBarGroup',
143
            'optimisticLocking'
144 74
        ];
145 74
        foreach ($toConfigure as $field) {
146 74
            if (array_key_exists($field, $crud)) {
147 74
                $function = 'set'.ucfirst($field);
148 74
                $definition->$function($crud[$field]);
149 74
            }
150 74
        }
151 74
    }
152
153
    /**
154
     * Creates and setups an EntityDefinition instance.
155
     *
156
     * @param Container $app
157
     * the application container
158
     * @param array $locales
159
     * the available locales
160
     * @param array $crud
161
     * the parsed YAML of a CRUD entity
162
     * @param string $name
163
     * the name of the entity
164
     *
165
     * @return EntityDefinition
166
     * the EntityDefinition good to go
167
     */
168 74
    protected function createDefinition(Container $app, array $locales, array $crud, $name)
169
    {
170 74
        $label               = array_key_exists('label', $crud) ? $crud['label'] : $name;
171 74
        $localeLabels        = $this->getLocaleLabels($locales, $crud);
172
        $standardFieldLabels = [
173 74
            'id' => $app['translator']->trans('crudlex.label.id'),
174 74
            'created_at' => $app['translator']->trans('crudlex.label.created_at'),
175 74
            'updated_at' => $app['translator']->trans('crudlex.label.updated_at')
176 74
        ];
177
178 74
        $factory = $app->offsetExists('crud.entitydefinitionfactory') ? $app['crud.entitydefinitionfactory'] : new EntityDefinitionFactory();
179
180 74
        $definition = $factory->createEntityDefinition(
181 74
            $crud['table'],
182 74
            $crud['fields'],
183 74
            $label,
184 74
            $localeLabels,
185 74
            $standardFieldLabels,
186
            $this
187 74
        );
188 74
        $this->configureDefinition($definition, $crud);
189 74
        return $definition;
190
    }
191
192
    /**
193
     * Validates the parsed entity definition.
194
     *
195
     * @param Container $app
196
     * the application container
197
     * @param array $entityDefinition
198
     * the entity definition to validate
199
     */
200 74
    protected function validateEntityDefinition(Container $app, array $entityDefinition)
201
    {
202 74
        $doValidate = !$app->offsetExists('crud.validateentitydefinition') || $app['crud.validateentitydefinition'] === true;
203 74
        if ($doValidate) {
204 73
            $validator = $app->offsetExists('crud.entitydefinitionvalidator')
205 73
                ? $app['crud.entitydefinitionvalidator']
206 73
                : new EntityDefinitionValidator();
207 73
            $validator->validate($entityDefinition);
208 73
        }
209 74
    }
210
211
    /**
212
     * Initializes the instance.
213
     *
214
     * @param string|null $crudFileCachingDirectory
215
     * the writable directory to store the CRUD YAML file cache
216
     * @param Container $app
217
     * the application container
218
     */
219 75
    public function init($crudFileCachingDirectory, Container $app)
220
    {
221
222 75
        $reader     = new YamlReader($crudFileCachingDirectory);
223 75
        $parsedYaml = $reader->read($app['crud.file']);
224
225 74
        $this->validateEntityDefinition($app, $parsedYaml);
226
227 74
        $locales     = $this->initLocales($app);
228 74
        $this->datas = [];
229 74
        foreach ($parsedYaml as $name => $crud) {
230 74
            $definition         = $this->createDefinition($app, $locales, $crud, $name);
231 74
            $this->datas[$name] = $app['crud.datafactory']->createData($definition, $app['crud.filesystem']);
232 74
        }
233
234 74
        $this->initChildren();
235
236 74
    }
237
238
    /**
239
     * Implements ServiceProviderInterface::register() registering $app['crud'].
240
     * $app['crud'] contains an instance of the ServiceProvider afterwards.
241
     *
242
     * @param Container $app
243
     * the Container instance of the Silex application
244
     */
245 11
    public function register(Container $app)
246
    {
247 11
        if (!$app->offsetExists('crud.filesystem')) {
248 11
            $app['crud.filesystem'] = new Filesystem(new Local(getcwd()));
249 11
        }
250 11
        $app['crud'] = function() use ($app) {
251 11
            $result                   = new static();
252 11
            $crudFileCachingDirectory = $app->offsetExists('crud.filecachingdirectory') ? $app['crud.filecachingdirectory'] : null;
253 11
            $result->init($crudFileCachingDirectory, $app);
254 11
            return $result;
255
        };
256 11
    }
257
258
    /**
259
     * Initializes the crud service right after boot.
260
     *
261
     * @param Application $app
262
     * the Container instance of the Silex application
263
     */
264 75
    public function boot(Application $app)
265
    {
266 75
        $this->initMissingServiceProviders($app);
267 75
        $twigExtensions = new TwigExtensions();
268 75
        $twigExtensions->registerTwigExtensions($app);
269 75
    }
270
271
    /**
272
     * Getter for the AbstractData instances.
273
     *
274
     * @param string $name
275
     * the entity name of the desired Data instance
276
     *
277
     * @return AbstractData
278
     * the AbstractData instance or null on invalid name
279
     */
280 68
    public function getData($name)
281
    {
282 68
        if (!array_key_exists($name, $this->datas)) {
283 8
            return null;
284
        }
285 68
        return $this->datas[$name];
286
    }
287
288
    /**
289
     * Getter for all available entity names.
290
     *
291
     * @return string[]
292
     * a list of all available entity names
293
     */
294 3
    public function getEntities()
295
    {
296 3
        return array_keys($this->datas);
297
    }
298
299
    /**
300
     * Getter for the entities for the navigation bar.
301
     *
302
     * @return string[]
303
     * a list of all available entity names with their group
304
     */
305 10
    public function getEntitiesNavBar()
306
    {
307 10
        $result = [];
308 10
        foreach ($this->datas as $entity => $data) {
309 10
            $navBarGroup = $data->getDefinition()->getNavBarGroup();
310 10
            if ($navBarGroup !== 'main') {
311 10
                $result[$navBarGroup][] = $entity;
312 10
            } else {
313
                $result[$entity] = 'main';
314
            }
315 10
        }
316 10
        return $result;
317
    }
318
319
    /**
320
     * Determines the Twig template to use for the given parameters depending on
321
     * the existance of certain keys in the Container $app in this order:
322
     *
323
     * crud.$section.$action.$entity
324
     * crud.$section.$action
325
     * crud.$section
326
     *
327
     * If nothing exists, this string is returned: "@crud/<action>.twig"
328
     *
329
     * @param Container $app
330
     * the Silex application
331
     * @param string $section
332
     * the section of the template, either "layout" or "template"
333
     * @param string $action
334
     * the current calling action like "create" or "show"
335
     * @param string $entity
336
     * the current calling entity
337
     *
338
     * @return string
339
     * the best fitting template
340
     */
341 11
    public function getTemplate(Container $app, $section, $action, $entity)
342
    {
343 11
        $crudSection       = 'crud.'.$section;
344 11
        $crudSectionAction = $crudSection.'.'.$action;
345
346
        $offsets = [
347 11
            $crudSectionAction.'.'.$entity,
348 11
            $crudSection.'.'.$entity,
349 11
            $crudSectionAction,
350
            $crudSection
351 11
        ];
352 11
        foreach ($offsets as $offset) {
353 11
            if ($app->offsetExists($offset)) {
354 9
                return $app[$offset];
355
            }
356 11
        }
357
358 11
        return '@crud/'.$action.'.twig';
359
    }
360
361
    /**
362
     * Sets the locale to be used.
363
     *
364
     * @param string $locale
365
     * the locale to be used.
366
     */
367 10
    public function setLocale($locale)
368
    {
369 10
        foreach ($this->datas as $data) {
370 10
            $data->getDefinition()->setLocale($locale);
371 10
        }
372 10
    }
373
374
    /**
375
     * Gets the available locales.
376
     *
377
     * @return array
378
     * the available locales
379
     */
380 75
    public function getLocales()
381
    {
382 75
        $localeDir     = __DIR__.'/../locales';
383 75
        $languageFiles = scandir($localeDir);
384 75
        $locales       = [];
385 75
        foreach ($languageFiles as $languageFile) {
386 75
            if (in_array($languageFile, ['.', '..'])) {
387 75
                continue;
388
            }
389 75
            $extensionPos = strpos($languageFile, '.yml');
390 75
            if ($extensionPos !== false) {
391 75
                $locale    = substr($languageFile, 0, $extensionPos);
392 75
                $locales[] = $locale;
393 75
            }
394 75
        }
395 75
        sort($locales);
396 75
        return $locales;
397
    }
398
399
}
400