Split my Symfony 4 application post

Split my Symfony 4 application into multiple libraries

Recently at work I had to split the code of one of our Symfony application for multiple reasons. But because Symfony 4 is Bundle-less, I had to find an other way to split.

A quick tour of my existing project

Initially I had my files organized by following the best practices of Symfony. It looked like this :

.
├── composer.json
├── config
├── src
│   ├── Command
│   │   ├── Part1
│   │   └── Part2
│   ├── Controller
│   │   ├── Part1
│   │   └── Part2
│   ├── Entity
│   │   ├── Part1
│   │   └── Part2
│   ├── Repository
│   │   ├── Part1
│   │   └── Part2
│   └── Service
│       ├── Part1
│       └── Part2
└── templates

As you can see, all the code is already spited but there are many issues :

  • the files are not totally spitted they just leave next to each other
  • there is often a merge conflict on the configuration files
  • I can't remove easily one part of my project
  • the dependencies are all mixed together (unique composer.json, ...)

Moving the files

So I've created a new folder libraries, and I'll put all my libraries in it. It looks like that:

.
├── composer.json
├── config
├── libraries
│   ├── Part1
│   │   ├── config
│   │   ├── src
│   │   │   ├── Command
│   │   │   ├── Entity
│   │   │   ├── Repository
│   │   │   ├── Service
│   │   │   └── composer.json
│   └── Part2
│   │   ├── config
│   │   ├── src
│   │   │   ├── Command
│   │   │   ├── Entity
│   │   │   ├── Repository
│   │   │   ├── Service
│   │   │   └── composer.json
├── src
│   ├── Controller
│   │   ├── Part1
│   │   └── Part2
└── templates

This migration is quite easy. You just have to move the files and update the namespace to match the new tree structure. For example, this old command:

<?php

namespace App\Command\Part1;

public class MyCommand {}

Will became :

<?php

namespace Part1\Command;

public class MyCommand {}

In each composer.json of the libraries, I've put the exact dependencies for the concerned librairy.

Here is the composer.json for my part1 library :

{
  "name": "my-project/part1",
  "description": "A library to do awesome things",
  "type": "library",
  "license": "proprietary",
  "minimum-stability": "dev",
  "prefer-stable": true,
  "autoload": {
    "psr-4": {
      "Part1\\": "src/"
    }
  },
  "require": {
    "php": "^7.1.3",
    ...
  },
  ...
}

Then the composer.json at the root of my project is almost empty, I just have to import my brand new libraries :

{
    "name": "my-project/core",
    "type": "project",
    "license": "proprietary",
    "require": {
        "my-project/part1": "*@dev",
        "my-project/part2": "*@dev",
    },
    "repositories": [
        {
            "type": "path",
            "url": "libraries/*"
        }
    ],
    ...
}

The magic happen in the repositories section. It said that there are new libraries in my library folder.

Then we need to configure our Symfony application !

Update the Symfony configuration files

Like I said in the beginning of this post, I want to separate my configurations, so I can remove the old ones quite easily. In my config/packages/ folder I've add a part1.yaml and a part2.yaml file which define all the configuration for my lib. For example here is my part1.yaml:

services:
    Part1\:
        resource: '%kernel.project_dir%/libraries/part1/src/*'
        exclude: '%kernel.project_dir%/libraries/part1/src/{Command,DataFixtures,DependencyInjection,Entity,Migrations,Repository,Tests,Kernel.php}'
        autowire: true

    # I have to redefined all that things, otherwise our application can't see them
    Part1\Repository\:
        resource: '%kernel.project_dir%/libraries/part1/src/Repository/*'
        autowire: true
        tags:
            - 'doctrine.repository_service'
    Part1\DataFixtures\:
        resource: '%kernel.project_dir%/libraries/part1/src/DataFixtures/*'
        autowire: true
        tags:
            - 'doctrine.fixture.orm'
    Part1\Command\:
        resource: '%kernel.project_dir%/libraries/part1/src/Command/*'
        autowire: true
        tags:
            - 'console.command'

# Then overload the services you need, here it's for doctrine but you can do it for everything
doctrine:
    orm:
        mappings:
            Part1:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/libraries/part1/src/Entity'
                prefix: 'Part1\Entity'
                alias: Part1

I've left the Controllers in my main src/ folder because for me belong here. And that's it ! Your application is now really spitted.

Categories: symfony

Tags: symfony, php, library