Completed
Push — master ( da52cb...88c0e7 )
by Philip
09:03 queued 04:20
created

ServiceProvider::getEntitiesNavBar()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

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