Completed
Push — master ( fcab66...68f1fa )
by Philip
05:57
created

ServiceProvider::getEntitiesNavBar()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

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

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
207
     * the factory to create the concrete AbstractData instances
208
     * @param string $crudFile
0 ignored issues
show
Documentation introduced by
There is no parameter named $crudFile. Did you maybe mean $crudFileCachingDirectory?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
209
     * the CRUD YAML file to parse
210
     * @param string|null $crudFileCachingDirectory
211
     * the writable directory to store the CRUD YAML file cache
212
     * @param Container $app
213
     * the application container
214
     */
215 75
    public function init($crudFileCachingDirectory, Container $app) {
216
217 75
        $reader     = new YamlReader($crudFileCachingDirectory);
218 75
        $parsedYaml = $reader->read($app['crud.file']);
219
220 74
        $this->validateEntityDefinition($app, $parsedYaml);
221
222 74
        $locales     = $this->initLocales($app);
223 74
        $this->datas = [];
224 74
        foreach ($parsedYaml as $name => $crud) {
225 74
            $definition         = $this->createDefinition($app, $locales, $crud, $name);
226 74
            $this->datas[$name] = $app['crud.datafactory']->createData($definition, $app['crud.filesystem']);
227
        }
228
229 74
        $this->initChildren();
230
231 74
    }
232
233
    /**
234
     * Implements ServiceProviderInterface::register() registering $app['crud'].
235
     * $app['crud'] contains an instance of the ServiceProvider afterwards.
236
     *
237
     * @param Container $app
238
     * the Container instance of the Silex application
239
     */
240 11
    public function register(Container $app) {
241 11
        if (!$app->offsetExists('crud.filesystem')) {
242 11
            $app['crud.filesystem'] = new Filesystem(new Local(getcwd()));
243
        }
244 11
        $app['crud'] = function() use ($app) {
245 11
            $result                   = new static();
246 11
            $crudFileCachingDirectory = $app->offsetExists('crud.filecachingdirectory') ? $app['crud.filecachingdirectory'] : null;
247 11
            $result->init($crudFileCachingDirectory, $app);
248 11
            return $result;
249
        };
250 11
    }
251
252
    /**
253
     * Initializes the crud service right after boot.
254
     *
255
     * @param Application $app
256
     * the Container instance of the Silex application
257
     */
258 75
    public function boot(Application $app) {
259 75
        $this->initMissingServiceProviders($app);
260 75
        $twigExtensions = new TwigExtensions();
261 75
        $twigExtensions->registerTwigExtensions($app);
262 75
    }
263
264
    /**
265
     * Getter for the {@see AbstractData} instances.
266
     *
267
     * @param string $name
268
     * the entity name of the desired Data instance
269
     *
270
     * @return AbstractData
271
     * the AbstractData instance or null on invalid name
272
     */
273 68
    public function getData($name) {
274 68
        if (!array_key_exists($name, $this->datas)) {
275 8
            return null;
276
        }
277 68
        return $this->datas[$name];
278
    }
279
280
    /**
281
     * Getter for all available entity names.
282
     *
283
     * @return string[]
284
     * a list of all available entity names
285
     */
286 3
    public function getEntities() {
287 3
        return array_keys($this->datas);
288
    }
289
290
    /**
291
     * Getter for the entities for the navigation bar.
292
     *
293
     * @return string[]
294
     * a list of all available entity names with their group
295
     */
296 10
    public function getEntitiesNavBar() {
297 10
        $result = [];
298 10
        foreach ($this->datas as $entity => $data) {
299 10
            $navBarGroup = $data->getDefinition()->getNavBarGroup();
300 10
            if ($navBarGroup !== 'main') {
301 10
                $result[$navBarGroup][] = $entity;
302
            } else {
303 10
                $result[$entity] = 'main';
304
            }
305
        }
306 10
        return $result;
307
    }
308
309
    /**
310
     * Determines the Twig template to use for the given parameters depending on
311
     * the existance of certain keys in the Container $app in this order:
312
     *
313
     * crud.$section.$action.$entity
314
     * crud.$section.$action
315
     * crud.$section
316
     *
317
     * If nothing exists, this string is returned: "@crud/<action>.twig"
318
     *
319
     * @param Container $app
320
     * the Silex application
321
     * @param string $section
322
     * the section of the template, either "layout" or "template"
323
     * @param string $action
324
     * the current calling action like "create" or "show"
325
     * @param string $entity
326
     * the current calling entity
327
     *
328
     * @return string
329
     * the best fitting template
330
     */
331 11
    public function getTemplate(Container $app, $section, $action, $entity) {
332 11
        $crudSection       = 'crud.'.$section;
333 11
        $crudSectionAction = $crudSection.'.'.$action;
334
335
        $offsets = [
336 11
            $crudSectionAction.'.'.$entity,
337 11
            $crudSection.'.'.$entity,
338 11
            $crudSectionAction,
339 11
            $crudSection
340
        ];
341 11
        foreach ($offsets as $offset) {
342 11
            if ($app->offsetExists($offset)) {
343 11
                return $app[$offset];
344
            }
345
        }
346
347 11
        return '@crud/'.$action.'.twig';
348
    }
349
350
    /**
351
     * Sets the locale to be used.
352
     *
353
     * @param string $locale
354
     * the locale to be used.
355
     */
356 10
    public function setLocale($locale) {
357 10
        foreach ($this->datas as $data) {
358 10
            $data->getDefinition()->setLocale($locale);
359
        }
360 10
    }
361
362
    /**
363
     * Gets the available locales.
364
     *
365
     * @return array
366
     * the available locales
367
     */
368 75
    public function getLocales() {
369 75
        $localeDir     = __DIR__.'/../locales';
370 75
        $languageFiles = scandir($localeDir);
371 75
        $locales       = [];
372 75
        foreach ($languageFiles as $languageFile) {
373 75
            if (in_array($languageFile, ['.', '..'])) {
374 75
                continue;
375
            }
376 75
            $extensionPos = strpos($languageFile, '.yml');
377 75
            if ($extensionPos !== false) {
378 75
                $locale    = substr($languageFile, 0, $extensionPos);
379 75
                $locales[] = $locale;
380
            }
381
        }
382 75
        sort($locales);
383 75
        return $locales;
384
    }
385
386
}
387