Working with BDD and resources in Sylius - Part 3, ResourceBundle

Mikołaj Król, 27 cze 2017

Let's start with building a bridge between your Book class and app_book table in your database. To achieve it, I will create a Doctrine entity definition in src/AppBundle/Resources/config/doctrine/Book.orm.yml file. I do prefer using yml over annotations because of not messing database and code definitions in one place. I also prefer yml over xml because of using one definition format for the whole application. This is the only area in my life where I can find some compromise. You should chose the one you feel more comfortable with.

#src/AppBundle/Resources/config/doctrine/Book.orm.yml

AppBundle\Entity\Book:
    type: entity
    table: app_book
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        title:
            type: string
            length: 255

As you should not use doctrine:schema:update --force command on your production server, let's use Doctrine migrations to migrate the current app state to the database.

  Because we have not installed the database from migrations, let's remove only one migration file in app/migrations directory to avoid TABLE EXISTS like errors when executing the migrations.

Run $ bin/console doctrine:migrations:diff and $ bin/console doctrine:migrations:migrate commands in this order which will first create new migration file in app/migrations folder and then execute the created migration queries to the database.

To add a new resource to the database, we will also need a form. Let's create a new one in AppBundle\Form\Type namespace:

namespace AppBundle\Form\Type;

use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

final class BookType extends AbstractResourceType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', TextType::class)
        ;
    }

    /**
    * {@inheritdoc}
    */
    public function getBlockPrefix()
    {
        return 'app_book';
    }
}
  As we are going to use it as resource type, this type should extend the AbstractResourceType which extends the default Symfony AbstractType and has some resource related logic in it. If you wonder why I have not written the spec for the BookType, read this great answer on Stackoverflow. Personally, I think that in order to write a good test for the form type in Symfony, you should consider using PHPUnit as it has more deeper dependencies. Good practice: Use PHPUnit for testing dependency injection and form type components (and anything with deeper functional dependencies) and PHPSpec for the plain logic of your application as well as simple Symfony related stuff like DataTransformers, EventListeners, etc.

There's one more thing that needs to be done for the form. It needs to be registered as a service. Add to your app/config/services.yml the following snippet:

services:
    app.book.form.type:
        class: AppBundle\Form\Type\BookType
        tags:
            - { name: form.type }
        arguments:
            - '%app.model.book.class%'
            - ['sylius']

Create the config.yml file in src/AppBundle/Resources/config destination and import this config file in app/config.yml by adding:

imports:
    ...
    - { resource: "@AppBundle/Resources/config/config.yml" }

under the imports keyword.

In the newly created config.yml file in your AppBundle write the following resource definition:

sylius_resource:
    resources:
        app.book:
            classes:
                model: AppBundle\Entity\Book
                form: AppBundle\Form\Type\Book

Let's now make this resource available from HTTP point of view by linking it to the routing file.

Create and fill the src/AppBundle/config/routing/admin.yml file with:

app_admin_book:
    resource: |
        alias: app.book
        section: admin
        templates: SyliusAdminBundle:Crud
        redirect: update
    type: sylius.resource

import this file in app/config/routing.yml by adding

app:
    resource: "@AppBundle/Resources/config/routing/admin.yml"
    prefix: /admin

on the bottom of the opened file.

  Run $ bin/console debug:router | grep app_admin_book and $ bin/console debug:container | grep .book and note how awesome the ResourceBundle is! Don't you like your interpreter to do some repetitive work for you? :)

And that's it. This is how it's done in Sylius. What do do next? I think you should take a look at the Sylius implementation of some tests which will give you the idea of directory structure and some nice code snippets you may use in future. Take a look at official Behat documentation, make yourself some coffee and read the PHPSpec manual as well.

I hope you enjoyed the way Sylius works and you are looking forward to using it in your projects or at least start using BDD. And hey, by the time I am writing this article, Sylius is in a late beta2 state. There are only a few more issues to close to start celebrating the stable release. Contribution is a great way to get some more experience in Behavioral-driven development process as well as many other areas of web development itself.

Sylius BDD - PHPSpec, 27 cze 2017 null, 17 lip 2017