Completed
Push — master ( e2a5de...1e0049 )
by Philip
02:18
created

CRUDServiceProvider::getLocaleLabels()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 3
eloc 6
nc 3
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 Silex\Application;
15
use Silex\ServiceProviderInterface;
16
use Symfony\Component\Yaml\Yaml;
17
use Symfony\Component\Translation\Loader\YamlFileLoader;
18
19
use CRUDlex\CRUDEntityDefinition;
20
use CRUDlex\CRUDDataFactoryInterface;
21
use CRUDlex\CRUDEntity;
22
use CRUDlex\CRUDFileProcessorInterface;
23
use CRUDlex\CRUDSimpleFilesystemFileProcessor;
24
25
/**
26
 * The CRUDServiceProvider setups and initializes the whole CRUD system.
27
 * After adding it to your Silex-setup, it offers access to {@see CRUDData}
28
 * instances, one for each defined entity off the CRUD YAML file.
29
 */
30
class CRUDServiceProvider implements ServiceProviderInterface {
31
32
    /**
33
     * Holds the {@see CRUDData} instances.
34
     */
35
    protected $datas;
36
37
    /**
38
     * Holds whether we manage the i18n.
39
     */
40
    protected $manageI18n;
41
42
    /**
43
     * Formats the given time value to a timestring defined by the $pattern
44
     * parameter.
45
     *
46
     * If the value is false (like null), an empty string is
47
     * returned. Else, the value is tried to be parsed as datetime via the
48
     * given pattern. If that fails, it is tried to be parsed with the pattern
49
     * 'Y-m-d H:i:s'. If that fails, the value is returned unchanged. Else, it
50
     * is returned formatted with the given pattern. The effect is to shorten
51
     * 'Y-m-d H:i:s' to 'Y-m-d' for example.
52
     *
53
     * @param string $value
54
     * the value to be formatted
55
     * @param string $pattern
56
     * the pattern with which the value is parsed and formatted
57
     *
58
     * @return string
59
     * the formatted value
60
     */
61
    protected function formatTime($value, $pattern) {
62
        if (!$value) {
63
            return '';
64
        }
65
        $result = \DateTime::createFromFormat($pattern, $value);
66
        if ($result === false) {
67
            $result = \DateTime::createFromFormat('Y-m-d H:i:s', $value);
68
        }
69
        if ($result === false) {
70
            return $value;
71
        }
72
        return $result->format($pattern);
73
    }
74
75
    /**
76
     * Reads and returns the contents of the given file. If
77
     * it goes wrong, it throws an exception.
78
     *
79
     * @param string $fileName
80
     * the file to read
81
     *
82
     * @return string
83
     * the file contents
84
     */
85
    protected function readYaml($fileName) {
86
        if (!file_exists($fileName) || !is_readable($fileName) || !is_file($fileName)) {
87
            throw new \Exception('Could not open CRUD file '.$fileName);
88
        }
89
        $fileContent = file_get_contents($fileName);
90
        return Yaml::parse($fileContent);
91
    }
92
93
    /**
94
     * Initializes needed but yet missing service providers.
95
     *
96
     * @param Application $app
97
     * the application container
98
     */
99
    protected function initMissingServiceProviders(Application $app) {
100
        if (!$app->offsetExists('translator')) {
101
            $app->register(new \Silex\Provider\TranslationServiceProvider(), array(
102
                'locale_fallbacks' => array('en'),
103
            ));
104
        }
105
106
        if (!$app->offsetExists('session')) {
107
            $app->register(new \Silex\Provider\SessionServiceProvider());
108
        }
109
110
        if (!$app->offsetExists('url_generator')) {
111
            $app->register(new \Silex\Provider\UrlGeneratorServiceProvider());
112
        }
113
114
        if (!$app->offsetExists('twig')) {
115
            $app->register(new \Silex\Provider\TwigServiceProvider());
116
            $app['twig.loader.filesystem']->addPath(__DIR__.'/../views/', 'crud');
117
        }
118
    }
119
120
    /**
121
     * Initializes the available locales.
122
     *
123
     * @param Application $app
124
     * the application container
125
     *
126
     * @return array
127
     * the available locales
128
     */
129
    protected function initLocales(Application $app) {
130
        $app['translator']->addLoader('yaml', new YamlFileLoader());
131
        $localeDir = __DIR__.'/../locales';
132
        $locales = $this->getLocales();
133
        foreach ($locales as $locale) {
134
            $app['translator']->addResource('yaml', $localeDir.'/'.$locale.'.yml', $locale);
135
        }
136
        return $locales;
137
    }
138
139
    /**
140
     * Initializes the children of the data entries.
141
     */
142
    protected function initChildren() {
143
        foreach ($this->datas as $name => $data) {
144
            $fields = $data->getDefinition()->getFieldNames();
145
            foreach ($fields as $field) {
146
                if ($data->getDefinition()->getType($field) == 'reference') {
147
                    $this->datas[$data->getDefinition()->getReferenceEntity($field)]->getDefinition()->addChild($data->getDefinition()->getTable(), $field, $name);
148
                }
149
            }
150
        }
151
    }
152
153
    /**
154
     * Gets a map with localized entity labels from the CRUD YML.
155
     *
156
     * @param array $locales
157
     * the available locales
158
     * @param array $crud
159
     * the CRUD entity map
160
     *
161
     * @return array
162
     * the map with localized entity labels
163
     */
164
    protected function getLocaleLabels($locales, $crud) {
165
        $localeLabels = array();
166
        foreach ($locales as $locale) {
167
            if (array_key_exists('label_'.$locale, $crud)) {
168
                $localeLabels[$locale] = $crud['label_'.$locale];
169
            }
170
        }
171
        return $localeLabels;
172
    }
173
174
    /**
175
     * Initializes the instance.
176
     *
177
     * @param CRUDDataFactoryInterface $dataFactory
178
     * the factory to create the concrete CRUDData instances
179
     * @param string $crudFile
180
     * the CRUD YAML file to parse
181
     * @param CRUDFileProcessorInterface $fileProcessor
182
     * the file processor used for file fields
183
     * @param boolean $manageI18n
184
     * holds whether we manage the i18n
185
     * @param Application $app
186
     * the application container
187
     */
188
    public function init(CRUDDataFactoryInterface $dataFactory, $crudFile, CRUDFileProcessorInterface $fileProcessor, $manageI18n, Application $app) {
189
190
        $this->manageI18n = $manageI18n;
191
192
        $this->initMissingServiceProviders($app);
193
        $locales = $this->initLocales($app);
194
195
        $parsedYaml = $this->readYaml($crudFile);
196
197
        $this->datas = array();
198
        foreach ((empty($parsedYaml) ? array() : $parsedYaml) as $name => $crud) {
199
            if (!is_array($crud) || !isset($crud['fields'])) {
200
                continue;
201
            }
202
203
            $label = array_key_exists('label', $crud) ? $crud['label'] : $name;
204
205
            $localeLabels = $this->getLocaleLabels($locales, $crud);
206
207
            $standardFieldLabels = array(
208
                'id' => $app['translator']->trans('crudlex.label.id'),
209
                'created_at' => $app['translator']->trans('crudlex.label.created_at'),
210
                'updated_at' => $app['translator']->trans('crudlex.label.updated_at')
211
            );
212
213
            $definition = new CRUDEntityDefinition(
214
                $crud['table'],
215
                $crud['fields'],
216
                $label,
217
                $localeLabels,
218
                $standardFieldLabels,
219
                $this
220
            );
221
            $this->datas[$name] = $dataFactory->createData($definition, $fileProcessor);
222
223
            if (array_key_exists('deleteCascade', $crud)) {
224
                $this->datas[$name]->getDefinition()->setDeleteCascade($crud['deleteCascade']);
225
            }
226
            if (array_key_exists('listFields', $crud)) {
227
                $this->datas[$name]->getDefinition()->setListFieldNames($crud['listFields']);
228
            }
229
            if (array_key_exists('filter', $crud)) {
230
                $this->datas[$name]->getDefinition()->setFilter($crud['filter']);
231
            }
232
            if (array_key_exists('childrenLabelFields', $crud)) {
233
                $this->datas[$name]->getDefinition()->setChildrenLabelFields($crud['childrenLabelFields']);
234
            }
235
            if (array_key_exists('pageSize', $crud)) {
236
                $this->datas[$name]->getDefinition()->setPageSize($crud['pageSize']);
237
            }
238
            if (array_key_exists('initialSortField', $crud)) {
239
                $this->datas[$name]->getDefinition()->setInitialSortField($crud['initialSortField']);
240
            }
241
            if (array_key_exists('initialSortAscending', $crud)) {
242
                $this->datas[$name]->getDefinition()->setInitialSortAscending($crud['initialSortAscending']);
243
            }
244
245
        }
246
247
        $this->initChildren();
248
249
    }
250
251
    /**
252
     * Implements ServiceProviderInterface::register() registering $app['crud'].
253
     * $app['crud'] contains an instance of the CRUDServiceProvider afterwards.
254
     *
255
     * @param Application $app
256
     * the Application instance of the Silex application
257
     */
258
    public function register(Application $app) {
259
        $app['crud'] = $app->share(function() use ($app) {
260
            $result = new CRUDServiceProvider();
261
            $fileProcessor = $app->offsetExists('crud.fileprocessor') ? $app['crud.fileprocessor'] : new CRUDSimpleFilesystemFileProcessor();
262
            $manageI18n = $app->offsetExists('crud.manageI18n') ? $app['crud.manageI18n'] : true;
263
            $result->init($app['crud.datafactory'], $app['crud.file'], $fileProcessor, $manageI18n, $app);
264
            return $result;
265
        });
266
    }
267
268
    /**
269
     * Implements ServiceProviderInterface::boot().
270
     *
271
     * @param Application $app
272
     * the Application instance of the Silex application
273
     */
274
    public function boot(Application $app) {
275
    }
276
277
    /**
278
     * Getter for the {@see CRUDData} instances.
279
     *
280
     * @param string $name
281
     * the entity name of the desired CRUDData instance
282
     *
283
     * @return CRUDData
284
     * the CRUDData instance or null on invalid name
285
     */
286
    public function getData($name) {
287
        if (!array_key_exists($name, $this->datas)) {
288
            return null;
289
        }
290
        return $this->datas[$name];
291
    }
292
293
    /**
294
     * Getter for all available entity names.
295
     *
296
     * @return array
297
     * a list of all available entity names
298
     */
299
    public function getEntities() {
300
        return array_keys($this->datas);
301
    }
302
303
    /**
304
     * Formats the given value to a date of the format 'Y-m-d'.
305
     *
306
     * @param string $value
307
     * the value, might be of the format 'Y-m-d H:i' or 'Y-m-d'
308
     *
309
     * @return string
310
     * the formatted result or an empty string on null value
311
     */
312
    public function formatDate($value) {
313
        return $this->formatTime($value, 'Y-m-d');
314
    }
315
316
    /**
317
     * Formats the given value to a date of the format 'Y-m-d H:i'.
318
     *
319
     * @param string $value
320
     * the value, might be of the format 'Y-m-d H:i'
321
     *
322
     * @return string
323
     * the formatted result or an empty string on null value
324
     */
325
    public function formatDateTime($value) {
326
        return $this->formatTime($value, 'Y-m-d H:i');
327
    }
328
329
    /**
330
     * Calls PHPs
331
     * {@link http://php.net/manual/en/function.basename.php basename} and
332
     * returns it's result.
333
     *
334
     * @param string $value
335
     * the value to be handed to basename
336
     *
337
     * @return string
338
     * the result of basename
339
     */
340
    public function basename($value) {
341
        return basename($value);
342
    }
343
344
    /**
345
     * Determines the Twig template to use for the given parameters depending on
346
     * the existance of certain keys in the Application $app in this order:
347
     *
348
     * crud.$section.$action.$entity
349
     * crud.$section.$action
350
     * crud.$section
351
     *
352
     * If nothing exists, this string is returned: "@crud/<action>.twig"
353
     *
354
     * @param Application $app
355
     * the Silex application
356
     * @param string $section
357
     * the section of the template, either "layout" or "template"
358
     * @param string $action
359
     * the current calling action like "create" or "show"
360
     * @param string $entity
361
     * the current calling entity
362
     *
363
     * @return string
364
     * the best fitting template
365
     */
366
    public function getTemplate(Application $app, $section, $action, $entity) {
367
        $crudSection = 'crud.'.$section;
368
        $crudSectionAction = $crudSection.'.'.$action;
369
370
        $offsets = array(
371
            $crudSectionAction.'.'.$entity,
372
            $crudSection.'.'.$entity,
373
            $crudSectionAction,
374
            $crudSection
375
        );
376
        foreach ($offsets as $offset) {
377
            if ($app->offsetExists($offset)) {
378
                return $app[$offset];
379
            }
380
        }
381
382
        return '@crud/'.$action.'.twig';
383
    }
384
385
    /**
386
     * Gets whether CRUDlex manages the i18n system.
387
     *
388
     * @return boolean
389
     * true if CRUDlex manages the i18n system
390
     */
391
    public function getManageI18n() {
392
        return $this->manageI18n;
393
    }
394
395
    /**
396
     * Sets the locale to be used.
397
     *
398
     * @param string $locale
399
     * the locale to be used.
400
     */
401
    public function setLocale($locale) {
402
        foreach ($this->datas as $data) {
403
            $data->getDefinition()->setLocale($locale);
404
        }
405
    }
406
407
    /**
408
     * Gets the available locales.
409
     *
410
     * @return array
411
     * the available locales
412
     */
413
    public function getLocales() {
414
        $localeDir = __DIR__.'/../locales';
415
        $languageFiles = scandir($localeDir);
416
        $locales = array();
417
        foreach ($languageFiles as $languageFile) {
418
            if ($languageFile == '.' || $languageFile == '..') {
419
                continue;
420
            }
421
            $extensionPos = strpos($languageFile, '.yml');
422
            if ($extensionPos !== false) {
423
                $locale = substr($languageFile, 0, $extensionPos);
424
                $locales[] = $locale;
425
            }
426
        }
427
        sort($locales);
428
        return $locales;
429
    }
430
431
    /**
432
     * Gets a language name in the given language.
433
     *
434
     * @param string $language
435
     * the language code of the desired language name
436
     *
437
     * @return string
438
     * the language name in the given language or null if not available
439
     */
440
    public function getLanguageName($language) {
441
        return \Symfony\Component\Intl\Intl::getLanguageBundle()->getLanguageName($language, $language, $language);
442
    }
443
444
    /**
445
     * Formats a float to not display in scientific notation.
446
     *
447
     * @param float $float
448
     * the float to format
449
     *
450
     * @return string
451
     * the formated float
452
     */
453
    public function formatFloat($float) {
454
455
        if (!$float) {
456
            return $float;
457
        }
458
459
        $zeroFraction = $float - floor($float) == 0 ? '0' : '';
460
461
        // We don't want values like 0.004 converted to  0.00400000000000000008
462
        if ($float > 0.0001) {
463
            return $float.($zeroFraction === '0' ? '.'.$zeroFraction : '');
464
        }
465
466
        // We don't want values like 0.00004 converted to its scientific notation 4.0E-5
467
        return rtrim(sprintf('%.20F', $float), '0').$zeroFraction;
468
    }
469
470
}
471