Elcodi Ecommerce Going Components (Symfony-based): Review and Startup your store!

Sometime ago I wrote a book on Symfony Ecommerce at http://pilotci.com. This blog post expands to cover a solution that has taken a good decision lately. The decision of Elcodi to go components. This is packages, somewhat decoupled at first but aiming to more decoupled type packages.

The quick way to see how a store can be put together with Elcodi components is by clonning its flag store https://github.com/elcodi/bamboo-store:

git clone git@github.com:elcodi/bamboo-store.git
cd bamboo-store
composer install

You will need my PR https://github.com/elcodi/bamboo-store/pull/29 to get the right dependencies and the basic store working.

Also you will need this https://github.com/elcodi/elcodi/pull/252/files. Because there is an error when the database is not setup and the command is set as a service.

After running creating the database and schema and running the fixtures:

~ php app/console doctrine:fixtures:load
Careful, database will be purged. Do you want to continue Y/N ?y
  > purging database
  > loading Elcodi\Bundle\LanguageBundle\DataFixtures\ORM\LanguageData
  > loading Elcodi\Bundle\AttributeBundle\DataFixtures\ORM\AttributeData
  > loading Elcodi\Bundle\RuleBundle\DataFixtures\ORM\ExpressionData
  > loading Elcodi\Bundle\MenuBundle\DataFixtures\ORM\NodeData
  > loading Elcodi\Bundle\UserBundle\DataFixtures\ORM\CustomerData
  > loading Elcodi\Bundle\BannerBundle\DataFixtures\ORM\BannerZoneData
  > loading Elcodi\Bundle\CurrencyBundle\DataFixtures\ORM\CurrencyData
  > loading Elcodi\Bundle\ProductBundle\DataFixtures\ORM\CategoryData
  > loading Elcodi\Bundle\ProductBundle\DataFixtures\ORM\ManufacturerData
  > loading Elcodi\Bundle\ProductBundle\DataFixtures\ORM\ProductData
  > loading Elcodi\Bundle\CartBundle\DataFixtures\ORM\CartData
  > loading Elcodi\Bundle\CouponBundle\DataFixtures\ORM\CouponData
  > loading Elcodi\Bundle\CartCouponBundle\DataFixtures\ORM\CartCouponData
  > loading Elcodi\Bundle\BannerBundle\DataFixtures\ORM\BannerData
  > loading Elcodi\Bundle\CurrencyBundle\DataFixtures\ORM\RatesData
  > loading Elcodi\Bundle\AttributeBundle\DataFixtures\ORM\ValueData
  > loading Elcodi\Bundle\ProductBundle\DataFixtures\ORM\VariantData
  > loading Elcodi\Bundle\RuleBundle\DataFixtures\ORM\RuleData
  > loading Elcodi\Bundle\RuleBundle\DataFixtures\ORM\RuleGroupData
  > loading Elcodi\Bundle\MenuBundle\DataFixtures\ORM\MenuData

You can see the store with php -S localhost:8000 -t web. And opening the browser on app_dev.php.

Screenshot 2014-08-28 18.57.30

The construction of the store is very simple and even though we can improve removing the nightmare of annotations, it seems like the power of the services is the real meat there:

    /**
     * Cart view
     *
     * @param FormView      $formView Form view
     * @param CartInterface $cart     Cart
     *
     * @return array
     *
     * @Route(
     *      path = "",
     *      name = "store_cart_view"
     * )
     * @Method("GET")
     * @Template
     *
     * @AnnotationEntity(
     *      class = {
     *          "factory" = "elcodi.cart_wrapper",
     *          "method" = "loadCart",
     *          "static" = false,
     *      },
     *      name = "cart"
     * )
     * @AnnotationForm(
     *      class = "store_cart_form_type_cart",
     *      name  = "formView",
     *      entity = "cart",
     * )
     */
    public function viewAction(
        FormView $formView,
        CartInterface $cart
    )
    {
        $relatedProducts = [];
 
        if ($cart->getCartLines()->count()) {
 
            $relatedProducts = $this
                ->get('store.product.service.product_collection_provider')
                ->getRelatedProducts($cart
                        ->getCartLines()
                        ->first()
                        ->getProduct()
                    , 3);
        }
 
        $cartCoupons = $this
            ->get('elcodi.cart_coupon_manager')
            ->getCartCoupons($cart);
 
        return [
            'cart'             => $cart,
            'cartcoupon'       => $cartCoupons,
            'form'             => $formView,
            'related_products' => $relatedProducts
        ];
    }

The annotations are basically boilerplate code for param converters and getting the form sorted out. All in all, it is spread out in various endpoints/links that do the work of the store. In the end is a clean simple store solution that does not get on the way. If you don’t understand or a component does not fit certain needs you can obliterate it by just unplugging the respective bundle. This latter of course can be further improved defining compiler passes in a respective package so that we can forget about bundles once and for all as it is the trend of modern developments.

The good thing is it is an open source project. I have seen various frameworks, sylius, vespolina, thelia, leaphly, akeneo and others, my advise is always to strive for simplicity and less strings attached. Readability of code is paramount because you cannot design with something you disagree with or cannot read very well. Just to name a few, vespolina is a bit dead, thelia is one unit and not exactly components, leaphly misses a bit of other components, and akeneo is more a backend, and sylius well it follows a similar approach to Elcodi and it has been for a bit longer but it is a bit hard to chew and challenging to implement features consistently with a different approach sometimes. All have pros and cons, but the idea of a good ecommerce is one does not get on the way, your app will be domain driven and so the components should just support your design not the other way around.

Next steps, homework, is to probably get rid of the annotations of the top layer in Elcodi bamboo and see how to make that more streamlined.

Hope you try elcodi!

New Symfony Way: Scratch Bundle for Packages with Compiler Passes

In the same way silex have service providers you can also have the same in Symfony, no need for bundles to define services. You can just plug some compiler passes and special configuration classes but really there is no need to create a bundle to have configuration capabilities.

The reasons for not having any bundle is that there is really no need for a bundle when you are creating an application package. Creating a bundle pulls your direction away from creating a reusable package outside symfony whereas if you create a standalone package and offer some service definitions it ends up being a better choice.

The addon which i have not mentioned is that there are really good projects out there like PageKit, even yucky projects like laravel that use symfony components or even others that do use the config and di components and yet do not use the bundle concept. For them the world of bundles is far away and will never be reached. Whereas if you design a library/package standalone it can have providers for these too. The end result is more reusability.

Let’s start with a good example. At the end of this blog from Matthias Noback’s blog post we have learned how to register ancillary event dispatcher services that are decoupled from the symfony default event_dispatcher system and how we register listeners or subscribers with that sub event system.

Here is the same code showing how you register the compiler pass in the way proposed in that blog post:

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
class YourBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(
            new RegisterListenersPass(
                'domain_event_dispatcher',
                'domain_event_listener',
                'domain_event_subscriber'
            ),
            PassConfig::TYPE_BEFORE_REMOVING
        );
    }
}

and add the service definitions:

services:
    domain_event_dispatcher:
        class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
        arguments:
            - @service_container

And how you create a listener (and extra bonus for the subscriber):

services:
    some_domain_event_listener
        class: ...
        tags:
            -
                name: domain_event_listener
                event: my_domain_event
                method: onMyDomainEvent
    some_domain_event_subscriber
        class: ...
        tags:
            -
                name: domain_event_subscriber

There is I believe another way to do this. What is truly required is a way to plug directly into the kernel (like it is done in bundles), the compiler passes that create this service definitions and then be done with it.

Let’s take a stab. First we can modify generically our AppKernel.php like this:

    protected function prepareContainer(ContainerBuilder $container)
    {
        $extensions = array();
        foreach ($this->bundles as $bundle) {
            if ($extension = $bundle->getContainerExtension()) {
                $container->registerExtension($extension);
                $extensions[] = $extension->getAlias();
            }
 
            if ($this->debug) {
                $container->addObjectResource($bundle);
            }
        }
        foreach ($this->bundles as $bundle) {
            $bundle->build($container);
        }
 
        $this->hookCompilers($container);
 
        // ensure these extensions are implicitly loaded
        $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
    }

Notice the hookCompilers is a custom method added:

    private function hookCompilers(ContainerBuilder $container)
    {
        foreach ($this->registerCompilers() as $compilerRow) {
            $instance = $compilerRow[0];
            $type = $compilerRow[1];
            $debug = $compilerRow[2];
            if (!$debug || $container->getParameter('kernel.debug')) {
                $container->addCompilerPass($instance, $type);
            }
        }
    }

But that will not need to change. The registerCompilers method is equivalent to the registerBundles method that is well known by the bundle system users. This registerCompilers method will look like:

    public function registerCompilers()
    {
        $configuration = new Configuration();
        $configuration->setEvaluateExpressions(true);
 
        return [
            [new ValidateServiceDefinitionsPass($configuration), PassConfig::TYPE_AFTER_REMOVING, true],
            [new FixValidatorDefinitionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, true],
            [new RegisterListenersPass(Dispatchers::READ, Dispatchers::READ_LISTENER, Dispatchers::READ_SUBSCRIBER), PassConfig::TYPE_BEFORE_REMOVING, false],
            [new RegisterListenersPass(Dispatchers::WRITE, Dispatchers::WRITE_LISTENER, Dispatchers::WRITE_SUBSCRIBER), PassConfig::TYPE_BEFORE_REMOVING, false],
        ];
    }

Notice that we have added several compiler passes here. The first two are coming from the package for validating the service definitions and stuff that we saw in a previous post. Now though they are totally standardized. The 2 last compiler passes added are two new event sub systems, easily pluggable just by adding new rows here. After this tapping into a different event dispatcher is totally straightforward.

Mind that we have just compiled the constants into a class Dispatchers.php:

<?php
 
namespace Grace;
 
class Dispatchers
{
    const READ = 'read_event_dispatcher';
    const READ_LISTENER = 'read_event_dispatcher_listener';
    const READ_SUBSCRIBER = 'read_event_dispatcher_subscriber';
 
    const WRITE = 'write_event_dispatcher';
    const WRITE_LISTENER = 'write_event_dispatcher_listener';
    const WRITE_SUBSCRIBER = 'write_event_dispatcher_subscriber';
}

You can now just define a subscriber like:

    grace.some_domain_event_subscriber:
        class: Grace\ExampleSubscriber
        tags:
            -
                name: Grace\Dispatchers::READ_SUBSCRIBER

The whole story can be better seen in a diff of this PR in matthew-7-12 project https://github.com/cordoval/matthew-7-12/pull/70/files.

A note on the logging capability of these new event dispatcher sub systems was made by @sstok. The concern is that on debug mode the event dispatcher is actually wrapped like this:

        <service id="debug.event_dispatcher" class="%debug.event_dispatcher.class%">
            <tag name="monolog.logger" channel="event" />
            <argument type="service" id="debug.event_dispatcher.parent" />
            <argument type="service" id="debug.stopwatch" />
            <argument type="service" id="logger" on-invalid="null" />
        </service>

Perhaps we can do a conditional and check for the interface they extend or class of the event dispatcher base that they extend and decorate accordingly our dispatchers too in case we are under the development mode. This of course is not hard to do and it could be accomplished by a DecorateRegisterListenerPasses.php compiler pass too.

An also good example on how to split a bundle and a dependency injection provider package is https://github.com/rollerworks/RollerworksSearchBundle. This bundle actually requires its dependency extension component in its composer.json:

    "require": {
        "php": ">=5.3.3",
        "rollerworks/search-symfony-di": "1.*@dev",
        "symfony/framework-bundle": "~2.1",
        "symfony/finder": "~2.1"
    },

Offering very good separation for those who integrate Rollerworks Search package without having to be tied to use a bundle. This is a better approach than the bundle one. You can take a look at the package here https://github.com/rollerworks/rollerworks-search-symfony-di/tree/master/src. In there you can also see a custom loader which is nice for when you go all the way with a bundle-less approach.

This approach will do great good to those systems that only use some symfony components and yet leverage their own component subsystem. Also for those projects that require flexibility, especially when dealing with DDD this is a desirable architecture to pursue.

Hope you like the approach, comment and don’t forget to retweet sil vous plait!

Alternative Method to Tame Symfony/Doctrine Repositories

Doctrine repositories are something the standard developer should know about, but how to really work well with repositories is hard to master without some good thought to it. Benjamin @beberlei wrote a blog post on taming repositories here http://www.whitewashing.de/2013/03/04/doctrine_repositories.html sometime ago. The ending result of its refactor creates several classes that are centered around Specification pattern that has a match method to which it gets passed the query builder, an object that is implementation specific. This creep however is somewhat unavoidable because of doctrine api and how it works. When you change implementation or use another library you have trashed all of this approach once and for all. Since this is very tied to implementation details I proposed a more quick and dirty approach that aims for versatility and could be easily implemented by anyone in less time. It is more deficient and quickly discardable and centers around quicker readable code.

With the previous approach one ends up with a bunch of classes and things like:

<?php
$specification = new Spec\AsArray(
    new Spec\AndX(
        new Spec\FilterGroup($groupId),
        new Spec\FilterPermission($permission)
    )
);
 
$groups = $app['orm.ems']['api']
    ->getRepository('\EasyBib\Api\Entity\Group')
    ->match($specification)
;

What if we make the specification more tuned in a single callable class:

$groups = $app['orm.ems']['api']
    ->getRepository('\EasyBib\Api\Entity\Group')
    ->match(new PowerUsers(15))
;

Then your user repository turns from:

<?php
class UserRepository
{
    public function match(UserSpecification $specification)
    {
        $qb = $this->createQueryBuilder('u');
        $expr = $specification->match($qb, 'u');
 
        $query = $qb->where($expr)->getQuery();
 
        $specification->modifyQuery($query);
 
        return $query->getResult();
    }
}

Into this:

<?php
class UserRepository
{
    public function match(callable $filter)
    {
        $qb = $this->createQueryBuilder('u');
 
        return call_user_func($filter, $qb);
    }
}

And all the things that build the query land into one place that gets encapsulated with a good old name:

<?php
class PowerUsers
{
    protected $limit;
 
    public function __construct($number)
    {
        $this->limit = $number;
    }
 
    public function __invoke(QueryBuilder $qb)
    {
        // here goes all the heavy lifting under one place
    }
}

This approach speaks for versatility, you can still compose reusability via the constructor if you may though the versatile aspect of this leaves the door open for more separation of concerns and composition but gives you the advantage of not spending too much time on prototyping phase. Also with some more sauce on it you can provide adapters for different implementations should you need to change gateways for doctrine odm or orm or other possibly.

I personally found to come up with more descriptive names so that controllers or services get slimmer and more readable.

Please feel free to leave your feedback and retweet this blog post. Thanks!

Symfony: Forget About Permission Settings

Ever wanted to forget about going into symfony.com looking for the configuration chapter of the book and copy pasting the long commands to set acl’s right on your systems? That is not only on your local but also on your server?

Well there was a discussion in a DX ticket that today I stumbled upon and here it is https://github.com/symfony/symfony/issues/11563 and skimming over it I found this pearl from @umpirski. It is called umpirsky/composer-permissions-handler and here it is how you set it up:

         "symfony/monolog-bundle": "~2.4",
         "sensio/distribution-bundle": "3.0.*@dev",
         "sensio/framework-extra-bundle": "~3.0",
-        "incenteev/composer-parameter-handler": "~2.0"
+        "incenteev/composer-parameter-handler": "~2.0",
+        "umpirsky/composer-permissions-handler": "dev-master"
     },
     "require-dev": {
         "sensio/generator-bundle": "~2.3"
 @@ -32,7 +33,8 @@
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
-            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles",
+            "Umpirsky\\PermissionsHandler\\ScriptHandler::setPermissions"
         ],
         "post-update-cmd": [
             "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
 @@ -49,6 +51,7 @@
     "extra": {
         "symfony-app-dir": "app",
         "symfony-web-dir": "web",
+        "writable-dirs": ["app/cache", "app/logs"],
         "incenteev-parameters": {
             "file": "app/config/parameters.yml"
         },

In mac it works like a charm and I am sure it works the same way in servers due to its nice phpspec’ed logic:

<?php
 
namespace Umpirsky\PermissionsHandler;
 
use Umpirsky\PermissionsHandler\Exception\PathNotFoundException;
 
class SetfaclPermissionsSetter extends PermissionsSetter
{
    public function setPermissions($path)
    {
        if (!is_dir($path)) {
            throw new PathNotFoundException($path);
        }
 
        $this->runCommand('setfacl -R -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path);
        $this->runCommand('setfacl -dR -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path);
    }
}

It is a very good example to learn and review phpspec niceties too!

Enjoy, thanks @umpirski for a nice package!

Decoupling in Symfony How to Pull out from Annotations

Annotations bring coupling as shown in Matthias Noback’s book on “A year with Symfony” and elsewhere in many blog posts around. You realize this too late if you are a newcomer because the documentation and many bundles assumed sometime ago that annotations was a good idea for automating stuff. This is the magic that Symfony in its beginnings set out itself to pursue but did not stick to as we are seeing lately in some developments. Easy of use can be sometimes a trap to add too much magic and blur the view of developers to use a library or framework.

Thanks to some efforts however there is a way in which you can pull your project out of annotations. One of them is using the doctrine bundle built-in capability to export mapping to yaml or other formats. The exporting seldom needs adjustments and even though some say it could be broken or lack some features, pretty much saves you a lot of time when you are doing the move with minor or no adjustments at all.

For instance if you have a

Vendor\CoreBundle

you can just issue this command:

php app/console doctrine:mapping:import "VendorCoreBundle" yml

The only thing with the output is that you will have to remove the indexes that it created and lifecyclecallbacks if you like to be clean and did not have anything specified in those fields.

It however generates a complete specification which helps you don’t miss anything:

// ...
            targetEntity: VendorUsers
            cascade: {  }
            fetch: LAZY
            mappedBy: null
            inversedBy: null
            joinColumns:
                locked_by_id:
                    referencedColumnName: id
            orphanRemoval: false
// ...

That is for mapping, but what about annotations. Well simply just install the bundle https://github.com/sed-szeged/SedRouteExporterBundle and run:

mkdir builds-output
php app/console sed:router:export --format yaml --output builds-output

The output is clogged into a single

routing.yml

file but it is easily split and cleaned up. The specification is also complete on the output:

user_lock:
    path: /users/lock
    defaults: { _controller: 'Vendor\Bundle\CoreBundle\Controller\UserController::lock' }
    options: { compiler_class: Symfony\Component\Routing\RouteCompiler }

Now your controllers will look much cleaner, you can actually go Controllers as Services and then have actual tests that cleanly tests part of your controllers at least. Definitely this will increase the quality of the code of your project because you are decoupling not only mapping, but routing, and others as well as you don’t need necessarily tools to convert but they speed up the process in converting something like this:

/**
 * @Cache(expires="tomorrow")
 * @Method(...)
 * @Template()
 * @ParamConverter()
 * @Route("some_route_here")
 * @Secure(roles="ROLE_USER")
 * @RunAs(roles="ROLE_PRIVATE_SERVICE")
 * @InjectParams({
 *     "em" = @Inject("doctrine.entity_manager")
 * })
 */
 */

Which is madness and that is just a quick invented example on how some projects end up. The conversion will heal you and your team.