Create a Symfony 4 shareable/reusable Bundle and take advantage of MakerBundle or any Symfony command line tool

Miguel Alcaino
6 min readMar 17, 2019

--

Hi there.

This article will go through the process of creating a Symfony reusable bundle and also it will explain how to take advantage of the makerBundle and other Symfony4 commands so the developing process of the bundle is faster.

It happened to me that I’ve been developing a Symfony Bundle that provides some entities and controllers to the main application when it’s imported. As I started this bundle when Symfony 3 was the current version and applications where developed in bundles, so my bundle was part of a Symfony 3 app and I could use the command line tool bin/console to generate a CRUD (bin/console generate:crud) or to use doctrine tools. But since the introduction of Symfony 4, bundles are gone in your main application and they are only valid when you import them in composer. The problem is that in Symfony 4 most of the available commands are supposed to work with what is inside the app and not outside, so every time I wanted to create a CRUD for an Entity in my bundle (in a Symfony 4 context) the make:crud command, and many other ones, were failing because they were expecting to read the entity and its context from inside the application and the App namespace.

So, as I haven’t seen any similar instructions around, I will document the steps I followed to have your reusable Symfony Bundle working with the command line tools provided by Symfony, so you can use make:entity or make:crud (provided by the MakerBundle) or any other command provided by Symfony and it won’t fail.

This tutorial assumes that you know how to create Symfony shareable/reusable bundles, if you don’t, check the official documentation here: https://symfony.com/doc/current/bundles/best_practices.html

Requirements

  • Symfony client. Run
# wget https://get.symfony.com/cli/installer -O - | bash

to get it. More info here https://symfony.com/download

General idea

What we are going to do is to create links from our bundle to a Symfony 4 app, so from the Symfony 4 app the command line tool will work and be able to write with no problems in your bundle. To be clear, let’s call your bundle as the bundle and the Symfony 4 app, where you are going to be able to execute commands, will be called the bundle maker app .

Have your Bundle ready

Let’s suppose that your Bundle provides Controllers, Entities, Repositories, Forms, Resources like views (templates) and translations, and injectable services. If your Bundle does not provide one of the functionalities mentioned above, don’t worry, just skip them when they are mentioned in this tutorial.

The source code inside the-bundle is inside the src folder. So it looks like this the-bundle/src .Take that into account when we link folders.

Create the containing folder

Create a folder called container-folder running mkdir container-folder you can call the folder however you want. It will contain both the bundle and the bundle maker app. So next step is to move the bundle folder inside the container-folder , you can do this:

  • Go inside your folder cd container-folder
  • Move the bundle foler inside the container-folder, running this mv /path/of/the-bundle .

Create the bundle maker app

Being inside your container-folder, let’s create a new Symfony 4 app (the bundle maker app). Run symfony new bundle-maker-app --full, wait until it finishes and you will have a brand new Symfony 4 app inside the bundle-maker-app folder. In the next steps we will modify the bundle-maker-app to make it work with your bundle.

At this point we should have two folders inside the container-folder :

  • bundle-maker-app
  • the-bundle

Changing the default namespace

The default namespace of a Symfony 4 app is App and in order to make it work with our bundle we need to change some files so the app understands that is not working with App anymore as the default namespace.

You can find your namespace inside the the-bundle/composer.json file. You probably know this, but just in case, it can be found inside the attribute autoload -> psr-4.

"autoload": {
"psr-4": {
"MiguelAlcaino\\CookingBundle\\": "src"
}
}

From the code above we understand that my default namespace is MiguelAlcaino\\CookingBundle\\ . So my php classes by default will be under the namespace MiguelAlcaino\CookingBundle.

Now let’s modify some files so the bundle-maker-app can forget about the App namespace.

composer.json

It looks like this

"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},

And it should look like this:

"autoload": {
"psr-4": {
"MiguelAlcaino\\CookingBundle\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"MiguelAlcaino\\CookingBundle\\Tests\\": "tests/"
}
},

.env.test

It looks like this

KERNEL_CLASS='App\Kernel'

And it should look like this

KERNEL_CLASS='MiguelAlcaino\CookingBundle\Kernel'

src/Kernel.php

It looks like this

<?php

namespace
App;

and it should look like this

<?php

namespace
MiguelAlcaino\CookingBundle;

public/index.php

It looks like this

<?php

use
App\Kernel;

and it should look like this

<?php

use
MiguelAlcaino\CookingBundle\Kernel;

config/services.yaml

It looks like this

services:
_defaults:
autowire: true
autoconfigure: true

App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'


App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']

Attention: I removed autowire, because autowire is not recommended in reusable bundles, as it is stated in the official documentation https://symfony.com/doc/current/service_container/autowiring.html#public-and-reusable-bundles . As my bundle provides Controllers autowired, I left the following code in my services.yaml file and removed everything else.

services:

MiguelAlcaino\CookingBundle\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']

If your bundle provides services, the services.yaml inside your bundle should be imported in the bundle-maker-app. So I added this at the beginning of the config/services.yaml file.

imports:
- { resource: cooking_bundle_services.yaml }

Any other config file that your bundle has should be imported as in the previous piece of code.

config/packages/doctrine.yaml

I looks like this

orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App

And it should look like this:

orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
mappings:
CookingPaymentsBundle:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'MiguelAlcaino\CookingBundle\Entity'
alias: MiguelAlcaino\CookingBundle

bin/console

It look like this

#!/usr/bin/env php
<?php

use
App\Kernel;

And it should look like this

#!/usr/bin/env php
<?php

use
MiguelAlcaino\CookingBundle\Kernel;

Create config/packages/dev/maker.yaml

To make the MakerBundle to work with the bundle’s namespace, you should let it know that your namespace is not App. So create the file config/packages/dev/maker.yaml and it should look look this:

maker:
root_namespace: 'MiguelAlcaino\CookingBundle'

Removing and linking

First we are going to remove the folders inside the bundle maker app that are going to be replaced by links pointing the bundle’s folders. So now we have to go inside the bundle-maker-app folder. Just run cd bundle-maker-app . Then run the following commands to remove the folders that will be replaced:

# rm -rf src/Controller
# rm -rf src/Entity
# rm -rf src/Repository
# rm -rf src/Migrations
# rm -rf templates
# rm -rf translations

Now we are going to create the links needed. The following table show how the links should be created. So let’s go doing the line one and the you can replicate it with the other ones you may need. Remember, I’m taking my bundle as example so your bundle may contain more or less folders, just link the ones needed. Also, as I mentioned earlier, the source code of my bundle is inside the src folder.

Run the following commands to actually create the links ( we are still inside the bundle-maker-app folder)

# ln -s ../the-bundle/src/Controller src/Controller
# ln -s ../the-bundle/src/Entity src/Entity
# ln -s ../the-bundle/src/Form src/Form
# ln -s ../the-bundle/src/Model src/Model
# ln -s ../the-bundle/src/Repository src/Repository
# ln -s ../the-bundle/src/Service src/Service
# ln -s ../the-bundle/src/Resources/views templates
# ln -s ../the-bundle/src/Resources/translations translations
# ln -s ../the-bundle/src/Resources/config/services.yaml config/cooking_bundle_services.yaml

Enjoy!

All done!. Now enjoy all the power of the command line tool to help you to develop your reusable bundle. Now everything is configured to work with the new namespace and folders.

You can try (being inside the bundle-maker-app)

# bin/console make:crud

and it should generate a CRUD for a given entity provided.

Cheers!

--

--