Completed
Push — master ( eae036...c36670 )
by Philip
06:28
created

ServiceProvider::getLocales()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 13
cts 13
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 0
crap 4
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 77
    protected function initMissingServiceProviders(Container $app)
47
    {
48
49 77
        if (!$app->offsetExists('translator')) {
50 77
            $app->register(new LocaleServiceProvider());
51 77
            $app->register(new TranslationServiceProvider(), [
52 77
                'locale_fallbacks' => ['en'],
53
            ]);
54
        }
55
56 77
        if (!$app->offsetExists('session')) {
57 67
            $app->register(new SessionServiceProvider());
58
        }
59
60 77
        if (!$app->offsetExists('twig')) {
61 67
            $app->register(new TwigServiceProvider());
62
        }
63 77
        $app['twig.loader.filesystem']->addPath(__DIR__.'/../views/', 'crud');
64 77
    }
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 76
    protected function initLocales(Container $app)
76
    {
77 76
        $locales   = $this->getLocales();
78 76
        $localeDir = __DIR__.'/../locales';
79 76
        $app['translator']->addLoader('yaml', new YamlFileLoader());
80 76
        foreach ($locales as $locale) {
81 76
            $app['translator']->addResource('yaml', $localeDir.'/'.$locale.'.yml', $locale);
82
        }
83 76
        return $locales;
84
    }
85
86
    /**
87
     * Initializes the children of the data entries.
88
     */
89 76
    protected function initChildren()
90
    {
91 76
        foreach ($this->datas as $name => $data) {
92 76
            $fields = $data->getDefinition()->getFieldNames();
93 76
            foreach ($fields as $field) {
94 76
                if ($data->getDefinition()->getType($field) == 'reference') {
95 76
                    $this->datas[$data->getDefinition()->getSubTypeField($field, 'reference', 'entity')]->getDefinition()->addChild($data->getDefinition()->getTable(), $field, $name);
96
                }
97
            }
98
        }
99 76
    }
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 76
    protected function getLocaleLabels(array $locales, array $crud)
113
    {
114 76
        $localeLabels = [];
115 76
        foreach ($locales as $locale) {
116 76
            if (array_key_exists('label_'.$locale, $crud)) {
117 76
                $localeLabels[$locale] = $crud['label_'.$locale];
118
            }
119
        }
120 76
        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 76
    protected function configureDefinition(EntityDefinition $definition, array $crud)
133
    {
134
        $toConfigure = [
135 76
            'deleteCascade',
136
            'listFields',
137
            'filter',
138
            'childrenLabelFields',
139
            'pageSize',
140
            'initialSortField',
141
            'initialSortAscending',
142
            'navBarGroup',
143
            'optimisticLocking'
144
        ];
145 76
        foreach ($toConfigure as $field) {
146 76
            if (array_key_exists($field, $crud)) {
147 76
                $function = 'set'.ucfirst($field);
148 76
                $definition->$function($crud[$field]);
149
            }
150
        }
151 76
    }
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 76
    protected function createDefinition(Container $app, array $locales, array $crud, $name)
169
    {
170 76
        $label               = array_key_exists('label', $crud) ? $crud['label'] : $name;
171 76
        $localeLabels        = $this->getLocaleLabels($locales, $crud);
172
        $standardFieldLabels = [
173 76
            'id' => $app['translator']->trans('crudlex.label.id'),
174 76
            'created_at' => $app['translator']->trans('crudlex.label.created_at'),
175 76
            'updated_at' => $app['translator']->trans('crudlex.label.updated_at')
176
        ];
177
178 76
        $factory = $app->offsetExists('crud.entitydefinitionfactory') ? $app['crud.entitydefinitionfactory'] : new EntityDefinitionFactory();
179
180 76
        $definition = $factory->createEntityDefinition(
181 76
            $crud['table'],
182 76
            $crud['fields'],
183 76
            $label,
184 76
            $localeLabels,
185 76
            $standardFieldLabels,
186 76
            $this
187
        );
188 76
        $this->configureDefinition($definition, $crud);
189 76
        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 76
    protected function validateEntityDefinition(Container $app, array $entityDefinition)
201
    {
202 76
        $doValidate = !$app->offsetExists('crud.validateentitydefinition') || $app['crud.validateentitydefinition'] === true;
203 76
        if ($doValidate) {
204 75
            $validator = $app->offsetExists('crud.entitydefinitionvalidator')
205 1
                ? $app['crud.entitydefinitionvalidator']
206 75
                : new EntityDefinitionValidator();
207 75
            $validator->validate($entityDefinition);
208
        }
209 76
    }
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 77
    public function init($crudFileCachingDirectory, Container $app)
220
    {
221
222 77
        $reader     = new YamlReader($crudFileCachingDirectory);
223 77
        $parsedYaml = $reader->read($app['crud.file']);
224
225 76
        $this->validateEntityDefinition($app, $parsedYaml);
226
227 76
        $locales     = $this->initLocales($app);
228 76
        $this->datas = [];
229 76
        foreach ($parsedYaml as $name => $crud) {
230 76
            $definition         = $this->createDefinition($app, $locales, $crud, $name);
231 76
            $this->datas[$name] = $app['crud.datafactory']->createData($definition, $app['crud.filesystem']);
232
        }
233
234 76
        $this->initChildren();
235
236 76
    }
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
        }
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 77
    public function boot(Application $app)
265
    {
266 77
        $this->initMissingServiceProviders($app);
267 77
        $twigExtensions = new TwigExtensions();
268 77
        $twigExtensions->registerTwigExtensions($app);
269 77
    }
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 70
    public function getData($name)
281
    {
282 70
        if (!array_key_exists($name, $this->datas)) {
283 8
            return null;
284
        }
285 70
        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
            } else {
313 10
                $result[$entity] = 'main';
314
            }
315
        }
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 11
            $crudSection
351
        ];
352 11
        foreach ($offsets as $offset) {
353 11
            if ($app->offsetExists($offset)) {
354 11
                return $app[$offset];
355
            }
356
        }
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
        }
372 10
    }
373
374
    /**
375
     * Gets the available locales.
376
     *
377
     * @return array
378
     * the available locales
379
     */
380 77
    public function getLocales()
381
    {
382 77
        $localeDir     = __DIR__.'/../locales';
383 77
        $languageFiles = scandir($localeDir);
384 77
        $locales       = [];
385 77
        foreach ($languageFiles as $languageFile) {
386 77
            if (in_array($languageFile, ['.', '..'])) {
387 77
                continue;
388
            }
389 77
            $extensionPos = strpos($languageFile, '.yml');
390 77
            if ($extensionPos !== false) {
391 77
                $locale    = substr($languageFile, 0, $extensionPos);
392 77
                $locales[] = $locale;
393
            }
394
        }
395 77
        sort($locales);
396 77
        return $locales;
397
    }
398
399
}
400