Completed
Push — master ( 2fbe0a...e2a5de )
by Philip
02:18
created

CRUDServiceProvider::initMissingServiceProviders()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 20
rs 8.8571
cc 5
eloc 11
nc 16
nop 1
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
     * Initializes the instance.
155
     *
156
     * @param CRUDDataFactoryInterface $dataFactory
157
     * the factory to create the concrete CRUDData instances
158
     * @param string $crudFile
159
     * the CRUD YAML file to parse
160
     * @param CRUDFileProcessorInterface $fileProcessor
161
     * the file processor used for file fields
162
     * @param boolean $manageI18n
163
     * holds whether we manage the i18n
164
     * @param Application $app
165
     * the application container
166
     */
167
    public function init(CRUDDataFactoryInterface $dataFactory, $crudFile, CRUDFileProcessorInterface $fileProcessor, $manageI18n, Application $app) {
168
169
        $this->manageI18n = $manageI18n;
170
171
        $this->initMissingServiceProviders($app);
172
        $locales = $this->initLocales($app);
173
174
        $parsedYaml = $this->readYaml($crudFile);
175
176
        $this->datas = array();
177
        foreach ((empty($parsedYaml) ? array() : $parsedYaml) as $name => $crud) {
178
            if (!is_array($crud) || !isset($crud['fields'])) {
179
                continue;
180
            }
181
182
            $label = array_key_exists('label', $crud) ? $crud['label'] : $name;
183
184
            $localeLabels = array();
185
            foreach ($locales as $locale) {
186
                if (array_key_exists('label_'.$locale, $crud)) {
187
                    $localeLabels[$locale] = $crud['label_'.$locale];
188
                }
189
            }
190
191
            $standardFieldLabels = array(
192
                'id' => $app['translator']->trans('crudlex.label.id'),
193
                'created_at' => $app['translator']->trans('crudlex.label.created_at'),
194
                'updated_at' => $app['translator']->trans('crudlex.label.updated_at')
195
            );
196
197
            $definition = new CRUDEntityDefinition(
198
                $crud['table'],
199
                $crud['fields'],
200
                $label,
201
                $localeLabels,
202
                $standardFieldLabels,
203
                $this
204
            );
205
            $this->datas[$name] = $dataFactory->createData($definition, $fileProcessor);
206
207
            if (array_key_exists('deleteCascade', $crud)) {
208
                $this->datas[$name]->getDefinition()->setDeleteCascade($crud['deleteCascade']);
209
            }
210
            if (array_key_exists('listFields', $crud)) {
211
                $this->datas[$name]->getDefinition()->setListFieldNames($crud['listFields']);
212
            }
213
            if (array_key_exists('filter', $crud)) {
214
                $this->datas[$name]->getDefinition()->setFilter($crud['filter']);
215
            }
216
            if (array_key_exists('childrenLabelFields', $crud)) {
217
                $this->datas[$name]->getDefinition()->setChildrenLabelFields($crud['childrenLabelFields']);
218
            }
219
            if (array_key_exists('pageSize', $crud)) {
220
                $this->datas[$name]->getDefinition()->setPageSize($crud['pageSize']);
221
            }
222
            if (array_key_exists('initialSortField', $crud)) {
223
                $this->datas[$name]->getDefinition()->setInitialSortField($crud['initialSortField']);
224
            }
225
            if (array_key_exists('initialSortAscending', $crud)) {
226
                $this->datas[$name]->getDefinition()->setInitialSortAscending($crud['initialSortAscending']);
227
            }
228
229
        }
230
231
        $this->initChildren();
232
233
    }
234
235
    /**
236
     * Implements ServiceProviderInterface::register() registering $app['crud'].
237
     * $app['crud'] contains an instance of the CRUDServiceProvider afterwards.
238
     *
239
     * @param Application $app
240
     * the Application instance of the Silex application
241
     */
242
    public function register(Application $app) {
243
        $app['crud'] = $app->share(function() use ($app) {
244
            $result = new CRUDServiceProvider();
245
            $fileProcessor = $app->offsetExists('crud.fileprocessor') ? $app['crud.fileprocessor'] : new CRUDSimpleFilesystemFileProcessor();
246
            $manageI18n = $app->offsetExists('crud.manageI18n') ? $app['crud.manageI18n'] : true;
247
            $result->init($app['crud.datafactory'], $app['crud.file'], $fileProcessor, $manageI18n, $app);
248
            return $result;
249
        });
250
    }
251
252
    /**
253
     * Implements ServiceProviderInterface::boot().
254
     *
255
     * @param Application $app
256
     * the Application instance of the Silex application
257
     */
258
    public function boot(Application $app) {
259
    }
260
261
    /**
262
     * Getter for the {@see CRUDData} instances.
263
     *
264
     * @param string $name
265
     * the entity name of the desired CRUDData instance
266
     *
267
     * @return CRUDData
268
     * the CRUDData instance or null on invalid name
269
     */
270
    public function getData($name) {
271
        if (!array_key_exists($name, $this->datas)) {
272
            return null;
273
        }
274
        return $this->datas[$name];
275
    }
276
277
    /**
278
     * Getter for all available entity names.
279
     *
280
     * @return array
281
     * a list of all available entity names
282
     */
283
    public function getEntities() {
284
        return array_keys($this->datas);
285
    }
286
287
    /**
288
     * Formats the given value to a date of the format 'Y-m-d'.
289
     *
290
     * @param string $value
291
     * the value, might be of the format 'Y-m-d H:i' or 'Y-m-d'
292
     *
293
     * @return string
294
     * the formatted result or an empty string on null value
295
     */
296
    public function formatDate($value) {
297
        return $this->formatTime($value, 'Y-m-d');
298
    }
299
300
    /**
301
     * Formats the given value to a date of the format 'Y-m-d H:i'.
302
     *
303
     * @param string $value
304
     * the value, might be of the format 'Y-m-d H:i'
305
     *
306
     * @return string
307
     * the formatted result or an empty string on null value
308
     */
309
    public function formatDateTime($value) {
310
        return $this->formatTime($value, 'Y-m-d H:i');
311
    }
312
313
    /**
314
     * Calls PHPs
315
     * {@link http://php.net/manual/en/function.basename.php basename} and
316
     * returns it's result.
317
     *
318
     * @param string $value
319
     * the value to be handed to basename
320
     *
321
     * @return string
322
     * the result of basename
323
     */
324
    public function basename($value) {
325
        return basename($value);
326
    }
327
328
    /**
329
     * Determines the Twig template to use for the given parameters depending on
330
     * the existance of certain keys in the Application $app in this order:
331
     *
332
     * crud.$section.$action.$entity
333
     * crud.$section.$action
334
     * crud.$section
335
     *
336
     * If nothing exists, this string is returned: "@crud/<action>.twig"
337
     *
338
     * @param Application $app
339
     * the Silex application
340
     * @param string $section
341
     * the section of the template, either "layout" or "template"
342
     * @param string $action
343
     * the current calling action like "create" or "show"
344
     * @param string $entity
345
     * the current calling entity
346
     *
347
     * @return string
348
     * the best fitting template
349
     */
350
    public function getTemplate(Application $app, $section, $action, $entity) {
351
        $crudSection = 'crud.'.$section;
352
        $crudSectionAction = $crudSection.'.'.$action;
353
354
        $offsets = array(
355
            $crudSectionAction.'.'.$entity,
356
            $crudSection.'.'.$entity,
357
            $crudSectionAction,
358
            $crudSection
359
        );
360
        foreach ($offsets as $offset) {
361
            if ($app->offsetExists($offset)) {
362
                return $app[$offset];
363
            }
364
        }
365
366
        return '@crud/'.$action.'.twig';
367
    }
368
369
    /**
370
     * Gets whether CRUDlex manages the i18n system.
371
     *
372
     * @return boolean
373
     * true if CRUDlex manages the i18n system
374
     */
375
    public function getManageI18n() {
376
        return $this->manageI18n;
377
    }
378
379
    /**
380
     * Sets the locale to be used.
381
     *
382
     * @param string $locale
383
     * the locale to be used.
384
     */
385
    public function setLocale($locale) {
386
        foreach ($this->datas as $data) {
387
            $data->getDefinition()->setLocale($locale);
388
        }
389
    }
390
391
    /**
392
     * Gets the available locales.
393
     *
394
     * @return array
395
     * the available locales
396
     */
397
    public function getLocales() {
398
        $localeDir = __DIR__.'/../locales';
399
        $languageFiles = scandir($localeDir);
400
        $locales = array();
401
        foreach ($languageFiles as $languageFile) {
402
            if ($languageFile == '.' || $languageFile == '..') {
403
                continue;
404
            }
405
            $extensionPos = strpos($languageFile, '.yml');
406
            if ($extensionPos !== false) {
407
                $locale = substr($languageFile, 0, $extensionPos);
408
                $locales[] = $locale;
409
            }
410
        }
411
        sort($locales);
412
        return $locales;
413
    }
414
415
    /**
416
     * Gets a language name in the given language.
417
     *
418
     * @param string $language
419
     * the language code of the desired language name
420
     *
421
     * @return string
422
     * the language name in the given language or null if not available
423
     */
424
    public function getLanguageName($language) {
425
        return \Symfony\Component\Intl\Intl::getLanguageBundle()->getLanguageName($language, $language, $language);
426
    }
427
428
    /**
429
     * Formats a float to not display in scientific notation.
430
     *
431
     * @param float $float
432
     * the float to format
433
     *
434
     * @return string
435
     * the formated float
436
     */
437
    public function formatFloat($float) {
438
439
        if (!$float) {
440
            return $float;
441
        }
442
443
        $zeroFraction = $float - floor($float) == 0 ? '0' : '';
444
445
        // We don't want values like 0.004 converted to  0.00400000000000000008
446
        if ($float > 0.0001) {
447
            return $float.($zeroFraction === '0' ? '.'.$zeroFraction : '');
448
        }
449
450
        // We don't want values like 0.00004 converted to its scientific notation 4.0E-5
451
        return rtrim(sprintf('%.20F', $float), '0').$zeroFraction;
452
    }
453
454
}
455