Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use container (and services) in Symfony 5 migrations?

I'm trying to get the container - and a service through it - in a migration (using Symfony 5.1.2 and doctrine/migrations 3.0.1). But when I try to create a class based on this example like this:

class Version20200717072537 extends AbstractMigration implements ContainerAwareInterface
{
    use ContainerAwareTrait;

    public function postUp(Schema $schema) : void
    {
        $fooService = $this->container->get('app.foo');
        //...
    }
}

... and I try to run migrations, I get a Call to a member function get() on null error.
So for some reason container is null.

I also tried replacing the use ContainerAwareTrait; with the actual setContainer function that I found in it, and it looks like the setContainer function is never called.

I'm pretty sure some time ago I was able to get the container from the migrations (maybe in Symfony 3 or 4?) based on the example I linked above, but now I don't know if this is the expected behavior in Symfony 5, or a bug, or I am doing something wrong.

like image 815
frzsombor Avatar asked Jul 17 '20 10:07

frzsombor


People also ask

What is a Symfony container?

In Symfony, these useful objects are called services and each service lives inside a very special object called the service container. The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!

What are migrations in Symfony?

Database migrations are a way to safely update your database schema both locally and on production. Instead of running the doctrine:schema:update command or applying the database changes manually with SQL statements, migrations allow to replicate the changes in your database schema in a safe manner.

What is a doctrine migration?

The Doctrine Migrations offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and powerful tool. In order to use migrations you need to do some setup first.

What is a entity in Symfony?

Well, entity is a type of object that is used to hold data. Each instance of entity holds exactly one row of targeted database table. As for the directories, Symfony2 has some expectations where to find classes - that goes for entities as well.


1 Answers

As mentioned in the comments documentation related to the old version. But now you can do it with MigrationFactory.

You need to create your own MigrationFactory class, which will inject some service into your migrations. Starts from 3.4 Symfony all services a private, so you can't get them from the container.

doctrine_migrations.yaml

services:
    'Doctrine\Migrations\Version\MigrationFactory': 'App\Doctrine\Migrations\MigrationFactory'

MigrationFactory

<?php

namespace App\Doctrine\Migrations;

use Doctrine\DBAL\Connection;
use Doctrine\Migrations\AbstractMigration;
use Psr\Log\LoggerInterface;

class MigrationFactory implements \Doctrine\Migrations\Version\MigrationFactory
{
    /** @var Connection */
    private $connection;

    /** @var LoggerInterface */
    private $logger;

    /**
     * @var SomeService
     */
    private $service;

    public function __construct(Connection $connection, LoggerInterface $logger, SomeService $service)
    {
        $this->connection = $connection;
        $this->logger     = $logger;
        $this->service = $service;
    }

    public function createVersion(string $migrationClassName) : AbstractMigration
    {
        $migration = new $migrationClassName(
            $this->connection,
            $this->logger
        );

        // or you can ommit this check
        if ($migration instanceof SomeInterface) {
            $migration->setService($this->service);
        }

        return $migration;
    }
}

Migration

<?php

declare(strict_types=1);

namespace App\Doctrine\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20200717112426 extends AbstractMigration implements SomeInterface
{
    private $service;

    public function setService(SomeService $service)
    {
        $this->service = $service;
    }

    public function getDescription() : string
    {
        return '';
    }

    public function up(Schema $schema) : void
    {
        // this up() migration is auto-generated, please modify it to your needs
    }

    public function postUp(Schema $schema): void
    {
        $this->service->someMethod();
    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs
    }
}
like image 184
Ihor Kostrov Avatar answered Oct 26 '22 05:10

Ihor Kostrov