Completed
Push — master ( 3b2785...de5258 )
by Philip
05:33
created

ServiceProvider::setManageI18n()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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