Completed
Push — master ( 97569c...236d2a )
by Philip
06:56
created

ServiceProvider::getLocaleLabels()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
nc 3
cc 3
eloc 6
nop 2
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
use Symfony\Component\Yaml\Yaml;
24
25
/**
26
 * The ServiceProvider setups and initializes the whole CRUD system.
27
 * After adding it to your Silex-setup, it offers access to {@see AbstractData}
28
 * instances, one for each defined entity off the CRUD YAML file.
29
 */
30
class ServiceProvider implements ServiceProviderInterface, BootableProviderInterface {
31
32
    /**
33
     * Holds the {@see AbstractData} instances.
34
     */
35
    protected $datas;
36
37
    /**
38
     * Initializes needed but yet missing service providers.
39
     *
40
     * @param Container $app
41
     * the application container
42
     */
43
    protected function initMissingServiceProviders(Container $app) {
44
45
        if (!$app->offsetExists('translator')) {
46
            $app->register(new LocaleServiceProvider());
47
            $app->register(new TranslationServiceProvider(), [
48
                'locale_fallbacks' => ['en'],
49
            ]);
50
        }
51
52
        if (!$app->offsetExists('session')) {
53
            $app->register(new SessionServiceProvider());
54
        }
55
56
        if (!$app->offsetExists('twig')) {
57
            $app->register(new TwigServiceProvider());
58
        }
59
        $app['twig.loader.filesystem']->addPath(__DIR__.'/../views/', 'crud');
60
    }
61
62
    /**
63
     * Initializes the available locales.
64
     *
65
     * @param Container $app
66
     * the application container
67
     *
68
     * @return array
69
     * the available locales
70
     */
71
    protected function initLocales(Container $app) {
72
        $locales   = $this->getLocales();
73
        $localeDir = __DIR__.'/../locales';
74
        $app['translator']->addLoader('yaml', new YamlFileLoader());
75
        foreach ($locales as $locale) {
76
            $app['translator']->addResource('yaml', $localeDir.'/'.$locale.'.yml', $locale);
77
        }
78
        return $locales;
79
    }
80
81
    /**
82
     * Initializes the children of the data entries.
83
     */
84
    protected function initChildren() {
85
        foreach ($this->datas as $name => $data) {
86
            $fields = $data->getDefinition()->getFieldNames();
87
            foreach ($fields as $field) {
88
                if ($data->getDefinition()->getType($field) == 'reference') {
89
                    $this->datas[$data->getDefinition()->getSubTypeField($field, 'reference', 'entity')]->getDefinition()->addChild($data->getDefinition()->getTable(), $field, $name);
90
                }
91
            }
92
        }
93
    }
94
95
    /**
96
     * Gets a map with localized entity labels from the CRUD YML.
97
     *
98
     * @param array $locales
99
     * the available locales
100
     * @param array $crud
101
     * the CRUD entity map
102
     *
103
     * @return array
104
     * the map with localized entity labels
105
     */
106
    protected function getLocaleLabels($locales, $crud) {
107
        $localeLabels = [];
108
        foreach ($locales as $locale) {
109
            if (array_key_exists('label_'.$locale, $crud)) {
110
                $localeLabels[$locale] = $crud['label_'.$locale];
111
            }
112
        }
113
        return $localeLabels;
114
    }
115
116
    /**
117
     * Configures the EntityDefinition according to the given
118
     * CRUD entity map.
119
     *
120
     * @param EntityDefinition $definition
121
     * the definition to configure
122
     * @param array $crud
123
     * the CRUD entity map
124
     */
125
    protected function configureDefinition(EntityDefinition $definition, array $crud) {
126
        $toConfigure = [
127
            'deleteCascade',
128
            'listFields',
129
            'filter',
130
            'childrenLabelFields',
131
            'pageSize',
132
            'initialSortField',
133
            'initialSortAscending'
134
        ];
135
        foreach ($toConfigure as $field) {
136
            if (array_key_exists($field, $crud)) {
137
                $function = 'set'.ucfirst($field);
138
                $definition->$function($crud[$field]);
139
            }
140
        }
141
    }
142
143
    /**
144
     * Creates and setups an EntityDefinition instance.
145
     *
146
     * @param Container $app
147
     * the application container
148
     * @param array $locales
149
     * the available locales
150
     * @param array $crud
151
     * the parsed YAML of a CRUD entity
152
     * @param string $name
153
     * the name of the entity
154
     *
155
     * @return EntityDefinition
156
     * the EntityDefinition good to go
157
     */
158
    protected function createDefinition(Container $app, array $locales, array $crud, $name) {
159
        $label               = array_key_exists('label', $crud) ? $crud['label'] : $name;
160
        $localeLabels        = $this->getLocaleLabels($locales, $crud);
161
        $standardFieldLabels = [
162
            'id' => $app['translator']->trans('crudlex.label.id'),
163
            'created_at' => $app['translator']->trans('crudlex.label.created_at'),
164
            'updated_at' => $app['translator']->trans('crudlex.label.updated_at')
165
        ];
166
167
        $factory = $app->offsetExists('crud.entitydefinitionfactory') ? $app['crud.entitydefinitionfactory'] : new EntityDefinitionFactory();
168
169
        $definition = $factory->createEntityDefinition(
170
            $crud['table'],
171
            $crud['fields'],
172
            $label,
173
            $localeLabels,
174
            $standardFieldLabels,
175
            $this
176
        );
177
        $this->configureDefinition($definition, $crud);
178
        return $definition;
179
    }
180
181
    /**
182
     * Validates the parsed entity definition.
183
     *
184
     * @param Container $app
185
     * the application container
186
     * @param array $entityDefinition
187
     * the entity definition to validate
188
     */
189
    protected function validateEntityDefinition(Container $app, array $entityDefinition) {
190
        $doValidate = !$app->offsetExists('crud.validateentitydefinition') || $app['crud.validateentitydefinition'] === true;
191
        if ($doValidate) {
192
            $validator = $app->offsetExists('crud.entitydefinitionvalidator')
193
                ? $app['crud.entitydefinitionvalidator']
194
                : new EntityDefinitionValidator();
195
            $validator->validate($entityDefinition);
196
        }
197
    }
198
199
    /**
200
     * Initializes the instance.
201
     *
202
     * @param DataFactoryInterface $dataFactory
203
     * the factory to create the concrete AbstractData instances
204
     * @param string $crudFile
205
     * the CRUD YAML file to parse
206
     * @param string|null $crudFileCachingDirectory
207
     * the writable directory to store the CRUD YAML file cache
208
     * @param FileProcessorInterface $fileProcessor
209
     * the file processor used for file fields
210
     * @param Container $app
211
     * the application container
212
     */
213
    public function init(DataFactoryInterface $dataFactory, $crudFile, $crudFileCachingDirectory, FileProcessorInterface $fileProcessor, Container $app) {
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $crudFileCachingDirectory exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
214
215
        $reader     = new YamlReader($crudFileCachingDirectory);
216
        $parsedYaml = $reader->read($crudFile);
217
218
        $this->validateEntityDefinition($app, $parsedYaml);
219
220
        $locales     = $this->initLocales($app);
221
        $this->datas = [];
222
        foreach ($parsedYaml as $name => $crud) {
223
            $definition         = $this->createDefinition($app, $locales, $crud, $name);
224
            $this->datas[$name] = $dataFactory->createData($definition, $fileProcessor);
225
        }
226
227
        $this->initChildren();
228
229
    }
230
231
    /**
232
     * Implements ServiceProviderInterface::register() registering $app['crud'].
233
     * $app['crud'] contains an instance of the ServiceProvider afterwards.
234
     *
235
     * @param Container $app
236
     * the Container instance of the Silex application
237
     */
238
    public function register(Container $app) {
239
        $app['crud'] = function() use ($app) {
240
            $result                   = new static();
241
            $crudFileCachingDirectory = $app->offsetExists('crud.filecachingdirectory') ? $app['crud.filecachingdirectory'] : null;
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $crudFileCachingDirectory exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
242
            $fileProcessor            = $app->offsetExists('crud.fileprocessor') ? $app['crud.fileprocessor'] : new SimpleFilesystemFileProcessor();
243
            $result->init($app['crud.datafactory'], $app['crud.file'], $crudFileCachingDirectory, $fileProcessor, $app);
244
            return $result;
245
        };
246
    }
247
248
    /**
249
     * Initializes the crud service right after boot.
250
     *
251
     * @param Application $app
252
     * the Container instance of the Silex application
253
     */
254
    public function boot(Application $app) {
255
        $this->initMissingServiceProviders($app);
256
        $twigExtensions = new TwigExtensions();
257
        $twigExtensions->registerTwigExtensions($app);
258
    }
259
260
    /**
261
     * Getter for the {@see AbstractData} instances.
262
     *
263
     * @param string $name
264
     * the entity name of the desired Data instance
265
     *
266
     * @return AbstractData
267
     * the AbstractData instance or null on invalid name
268
     */
269
    public function getData($name) {
270
        if (!array_key_exists($name, $this->datas)) {
271
            return null;
272
        }
273
        return $this->datas[$name];
274
    }
275
276
    /**
277
     * Getter for all available entity names.
278
     *
279
     * @return string[]
280
     * a list of all available entity names
281
     */
282
    public function getEntities() {
283
        return array_keys($this->datas);
284
    }
285
286
    /**
287
     * Determines the Twig template to use for the given parameters depending on
288
     * the existance of certain keys in the Container $app in this order:
289
     *
290
     * crud.$section.$action.$entity
291
     * crud.$section.$action
292
     * crud.$section
293
     *
294
     * If nothing exists, this string is returned: "@crud/<action>.twig"
295
     *
296
     * @param Container $app
297
     * the Silex application
298
     * @param string $section
299
     * the section of the template, either "layout" or "template"
300
     * @param string $action
301
     * the current calling action like "create" or "show"
302
     * @param string $entity
303
     * the current calling entity
304
     *
305
     * @return string
306
     * the best fitting template
307
     */
308
    public function getTemplate(Container $app, $section, $action, $entity) {
309
        $crudSection       = 'crud.'.$section;
310
        $crudSectionAction = $crudSection.'.'.$action;
311
312
        $offsets = [
313
            $crudSectionAction.'.'.$entity,
314
            $crudSection.'.'.$entity,
315
            $crudSectionAction,
316
            $crudSection
317
        ];
318
        foreach ($offsets as $offset) {
319
            if ($app->offsetExists($offset)) {
320
                return $app[$offset];
321
            }
322
        }
323
324
        return '@crud/'.$action.'.twig';
325
    }
326
327
    /**
328
     * Sets the locale to be used.
329
     *
330
     * @param string $locale
331
     * the locale to be used.
332
     */
333
    public function setLocale($locale) {
334
        foreach ($this->datas as $data) {
335
            $data->getDefinition()->setLocale($locale);
336
        }
337
    }
338
339
    /**
340
     * Gets the available locales.
341
     *
342
     * @return array
343
     * the available locales
344
     */
345
    public function getLocales() {
346
        $localeDir     = __DIR__.'/../locales';
347
        $languageFiles = scandir($localeDir);
348
        $locales       = [];
349
        foreach ($languageFiles as $languageFile) {
350
            if (in_array($languageFile, ['.', '..'])) {
351
                continue;
352
            }
353
            $extensionPos = strpos($languageFile, '.yml');
354
            if ($extensionPos !== false) {
355
                $locale    = substr($languageFile, 0, $extensionPos);
356
                $locales[] = $locale;
357
            }
358
        }
359
        sort($locales);
360
        return $locales;
361
    }
362
363
}
364