Extended Features

While continuing with the library example, we add some more features as we go.

Field Descriptions

Sometimes, you want to guide the user a bit more and be more descriptive than just a label on the details and editpage of an entity. To do so, you can set a description per field. In this case, the author of a book is more informative:

library:
    label: Library
    table: library
    fields:
        name:
            type: text
            label: Name
book:
    label: Book
    table: book
    fields:
        author:
            type: text
            label: Author
            description: The Author of the Book
        title:
            type: text
            label: Title
        pages:
            type: integer
            label: Pages

Displayed Fields in the List

As an addition, we only want to show the author and title in the big list view of the books. We can do it by using the listFields entry:

library:
    label: Library
    table: library
    fields:
        name:
            type: text
            label: Name
book:
    label: Book
    table: book
    listFields: [id, created_at, updated_at, author, title]
    fields:
        author:
            type: text
            label: Author
        title:
            type: text
            label: Title
        pages:
            type: integer
            label: Pages

It is a simple list referencing the fields. Note the usage of the internal fields “id”, “created_at” and “update_at”. “version” is not yet used and every row where “deleted_at” is not null is marked as deleted, so this field would make no sense to display.

Filters

Currently, the listview contains all entries on the pages. Often, it is desirable to filter it in order to search for specific entries. The fields to be allowed to filter on can be easily added with a filter array just like the listFields. This is how the books view could be filtered by author and title:

library:
    label: Library
    table: library
    pageSize: 5
    fields:
        name:
            type: text
            label: Name
book:
    label: Book
    table: book
    listFields: [id, created_at, updated_at, author, title]
    filter: [author, title]
    fields:
        author:
            type: text
            label: Author
        title:
            type: text
            label: Title
        pages:
            type: integer
            label: Pages

Hard Deletion

By default, CRUDlex uses a soft deletion mechanism by only setting a deleted_at field. Hard deletion from the database can be activated though via the “hardDeletion” flag like this:

library:
    table: library
    hardDeletion: true
    fields:
        name:
            type: text
            label: Name

If activated, the column “deleted_at” is not needed in the entities table.

Group entities in the Navigation Bar

Each entity represents and option in the navigation bar at the top. If there are too many entities or want to group based on relations between the entities, you can add a “navBarGroup” option. In this case we group library and books under “Learning Resources”:

library:
    label: Library
    table: library
    navBarGroup: Learning Resources
    fields:
        name:
            type: text
            label: Name
book:
    label: Book
    table: book
    navBarGroup: Learning Resources
    fields:
        author:
            type: text
            label: Author
        title:
            type: text
            label: Title
        pages:
            type: integer
            label: Pages

I18n

Here are some features around the i18n support.

Set the Translations of Entity- and Field-Labels

You can translate the labels of the entities and their fields using some special label keys: label_(locale) with (locale) being your desired locale. Example for de:

book:
    label: Book
    label_de: Buch
    table: book
    listFields: [id, created_at, updated_at, author, title]
    filter: [author, title]
    fields:
        author:
            type: text
            label: Author
            label_de: Autor
        title:
            type: text
            label: Title
            label_de: Titel
        pages:
            type: integer
            label: Pages
            label_de: Seiten

Switch off I18n Management

Per default, CRUDlex manages i18n for you. But this might be not desired in bigger projects with existing i18n management, so you can disable it on registration like this:

Place a call to setManageI18n within your crudlex.service definition:

crudlex.service:
    public: true
    class: "CRUDlex\\Service"
    arguments:
      - "%kernel.project_dir%/config/crud.yml"
      - "%kernel.cache_dir%"
      - "@Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface"
      - "@translator"
      - "@crudlex.dataFactoryInterface"
      - "@crudlex.entityDefinitionFactoryInterface"
      - "@crudlex.fileSystem"
      - "@crudlex.entityDefinitionValidatorInterface"
    calls:
      - method: setManageI18n
        arguments: [false]
$app['crud']->setManageI18n(false);

Initial Sorting Parameters

Initially, when you visit the list page of an entity, the view is sorted ascending by created_at. There might be cases, where you want to change that.

For this, two parameters can be set on entity level:

  • initialSortField: Sets the field the data is sort by
  • initialSortAscending: If set to true, the initial sort order is ascending, if set to false, the initial sort order is descending

Here is an example where the books are sorted by their author in an descending order:

book:
    label: Book
    table: book
    filter: [author, title]
    initialSortField: author
    initialSortAscending: false
    fields:
        author:
            type: text
            label: Author
        title:
            type: text
            label: Title
        pages:
            type: integer
            label: Pages

Attention: In the list view, it is not possible to sort by many fields as it doesn’t make that much sense.

Using UUIDs as Primary Key Instead of an Auto Incremented Value

CRUDMySQLData offers an option to use UUIDs as primary key instead of an auto incremented value.

First, you have to create your id field as varchar(36):

`id` varchar(36) NOT NULL

And then you have to activate it in the setup when creating the CRUDDataFactoryInterface:

crudlex.dataFactoryInterface:
    public: true
    class: "CRUDlex\\MySQLDataFactory"
    arguments:
      - "@doctrine.dbal.default_connection"
      - true
$dataFactory = new CRUDlex\MySQLDataFactory($app['db'], true);

Using the CRUD YAML file cache

Parsing the CRUD YAML file cache on each request can have an impact on the performance with bigger files. To mitigate this, CRUDlex can cache the parsed content to a PHP file. Using a PHP file has the advantage, that this is then cached for free by the opcode cache of PHP.

To activate the caching, you simply pass a path to a writable directory on registration:

With Symfony 4, this feature is already activated and writes to %kernel.cache_dir%. It can be changed or even deactivated by setting the service argument to an empty string like this:

crudlex.service:
    public: true
    class: "CRUDlex\\Service"
    arguments:
      - "%kernel.project_dir%/config/crud.yml"
      - ""
      - "@Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface"
      - "@translator"
      - "@crudlex.dataFactoryInterface"
      - "@crudlex.entityDefinitionFactoryInterface"
      - "@crudlex.fileSystem"
      - "@crudlex.entityDefinitionValidatorInterface"
$app->register(new CRUDlex\ServiceProvider(), [
    'crud.filecachingdirectory' => '/path/to/a/writable/directory'
]);

Note that CRUDlex doesn’t do any cache invalidation. You have to delete the cached files yourself if needed. This is the case if the crud.yml got modified for example.

Using an own Implementation of the EntityDefinition

There might be the case where you want to use an own implementation derived from the EntityDefinition. In this case, you can hand in an own implementation of the EntityDefinitionFactory like this:

crudlex.service:
    public: true
    class: "CRUDlex\\Service"
    arguments:
      - "%kernel.project_dir%/config/crud.yml"
      - "%kernel.cache_dir%"
      - "@Symfony\\Component\\Routing\\Generator\\UrlGeneratorInterface"
      - "@translator"
      - "@crudlex.dataFactoryInterface"
      - "@MyOwnEntityDefinitionFactoryService"
      - "@crudlex.fileSystem"
      - "@crudlex.entityDefinitionValidatorInterface"
$entityDefinitionFactory = new MyOwnEntityDefinitionFactory();
$app->register(new CRUDlex\ServiceProvider(), [
    'crud.entitydefinitionfactory' => $entityDefinitionFactory
]);

Prepopulated Form Fields on the Creation Page

You can set some initial values when you link the creation page from somewhere else by handing in the appropriate GET parameter. Example for the author of a book: …/book/create?author=MyAuthor

Combine with the Web Profiler

If you want to use the package “silex/web-profiler”, you have to register the LocaleServiceProvider and TranslationServiceProvider on your own first:

$app->register(new Silex\Provider\LocaleServiceProvider());
$app->register(new \Silex\Provider\TranslationServiceProvider(), array(
    'locale_fallbacks' => array('en'),
));

Serving Static Content via the Webserver

It might be beneficial to serve the static content like CSS files directly via the webserver as it has some performance advantages.

Both solutions assume that the folder vendor/philiplb/crudlex/src/static is accessible. <mountPath> is the path the ControllerProvider is mounted to.

Apache

The requests to the static route can be redirected to the static files:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{QUERY_STRING} ^file=(.*)$
RewriteRule <mountPath>/resource/static$ vendor/philiplb/crudlex/src/static/%1 [QSA,L]

nginx

A location with the try_files directive can serve the static files like this:

location /<mountPath>/resource/static {
    try_files /vendor/philiplb/crudlex/src/static/$arg_file =404;
}