Completed
Push — master ( 68f1fa...1cb896 )
by Philip
06:06
created

ServiceProvider::getLocaleLabels()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

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