2013-02-04 11 views
5

Stiamo creando un comando che si basa su altri comandi per generare un nuovo database e creare il suo schema. Finora l'abbiamo ottenuto con successo per leggere il file config.yml, aggiungere le nostre nuove informazioni di connessione e per scrivere di nuovo il file. Nello stesso comando cerchiamo quindi di eseguire i comandi di symfony per creare il database e lo schema: aggiornamento. Questo è dove stiamo incontrando problemi. Otteniamo il seguente errore:Symfony 2: accesso alla configurazione aggiornata da un comando

[InvalidArgumentException] Doctrine ORM Manager named "mynewdatabase" does not exist.

Se lanciamo il comando una seconda volta non v'è alcun errore perché il file di configurazione aggiornato viene caricato fresca nell'applicazione. Se eseguiamo manualmente i comandi doctrine dopo aver scritto nel file config.yml, funziona anche senza errori.

Stiamo pensando che al punto nel nostro comando in cui stiamo eseguendo il database di creare e aggiornare i comandi, sta ancora utilizzando la versione corrente del kernel di config.yml/database.yml che sono memorizzati in memoria. Abbiamo provato diversi modi per reinizializzare la configurazione dell'applicazione/del kernel (chiamando shutdown(), boot(), ecc.) Senza fortuna. Ecco il codice:

namespace Test\MyBundle\Command; 

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 
use Symfony\Component\Console\Input\InputArgument; 
use Symfony\Component\Console\Input\InputInterface; 
use Symfony\Component\Console\Input\InputOption; 
use Symfony\Component\Console\Input\ArrayInput; 
use Symfony\Component\Console\Output\OutputInterface; 
use Symfony\Component\Yaml\Yaml; 

class GeneratorCommand extends ContainerAwareCommand 
{ 
    protected function configure() 
    { 
     $this 
      ->setName('generate') 
      ->setDescription('Create a new database.') 
      ->addArgument('dbname', InputArgument::REQUIRED, 'The db name') 
     ; 
    } 

    /* 
     example: php app/console generate mynewdatabase 
    */ 
    protected function execute(InputInterface $input, OutputInterface $output) 
    { 
     //Without this, the doctrine commands will prematurely end execution 
     $this->getApplication()->setAutoExit(false); 

     //Open up app/config/config.yml 
     $yaml = Yaml::parse(file_get_contents($this->getContainer()->get('kernel')->getRootDir() .'/config/config.yml')); 

     //Take input dbname and use it to name the database 
     $db_name = $input->getArgument('dbname'); 

     //Add that connection to app/config/config.yml 
     $yaml['doctrine']['dbal']['connections'][$site_name] = Array('driver' => '%database_driver%', 'host' => '%database_host%', 'port' => '%database_port%', 'dbname' => $site_name, 'user' => '%database_user%', 'password' => '%database_password%', 'charset' => 'UTF8'); 
     $yaml['doctrine']['orm']['entity_managers'][$site_name] = Array('connection' => $site_name, 'mappings' => Array('MyCustomerBundle' => null)); 

     //Now put it back 
     $new_yaml = Yaml::dump($yaml, 5); 
     file_put_contents($this->getContainer()->get('kernel')->getRootDir() .'/config/config.yml', $new_yaml); 

     /* http://symfony.com/doc/current/components/console/introduction.html#calling-an-existing-command */ 

     //Set up our db create script arguments 
     $args = array(
     'command'  => 'doctrine:database:create', 
     '--connection' => $site_name, 
    ); 
     $db_create_input = new ArrayInput($args); 

     //Run the symfony database create arguments 
     $this->getApplication()->run($db_create_input, $output); 

     //Set up our schema update script arguments 
     $args = array(
     'command' => 'doctrine:schema:update', 
     '--em'  => $site_name, 
     '--force' => true 
    ); 
     $update_schema_input = new ArrayInput($args); 

     //Run the symfony database create command 
     $this->getApplication()->run($update_schema_input, $output); 
    } 
} 

risposta

3

La ragione per cui questo non funziona è perché il DIC passa attraverso un processo di compilazione e viene scritta in un file PHP che viene poi incluso nel processo attualmente in esecuzione. Che potete vedere qui:

https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Kernel.php#L562

Se si modificano le definizioni di servizi e quindi si tenta di "reboot" il kernel per compilare questi cambiamenti non includerà il file compilato una seconda volta (require_once) e creerà solo un'altra istanza della classe DIC già inclusa con le vecchie definizioni del servizio compilate.

Il modo più semplice che posso immaginare per aggirare questo è creare una classe Kernel vuota che estenda semplicemente l'AppKernel. In questo modo:

<?php 

namespace Test\MyBundle\Command; 

class FakeKernel extends \AppKernel 
{ 
} 

Poi, nel tuo comando, si può avviare questo kernel dopo aver salvato le nuove definizioni di servizio e sarà ri-compilare una nuova classe DIC utilizzando il nome "FakeKernel" come parte del nome del file che significa che sarà essere incluso. in questo modo:

$kernel = new \Test\MyBundle\Command\FakeKernel($input->getOption('env'), true); 
$application = new \Symfony\Bundle\FrameworkBundle\Console\Application($kernel); 

Poi si esegue il sotto-comandi contro questa nuova applicazione, che sarà in esecuzione con il nuovo DIC:

$application->run($db_create_input, $output); 

disclaimer: questo si sente molto hacky. Sono aperto a sentire altre soluzioni/soluzioni alternative.

+0

Matt il tuo suggerimento è azzeccato. Alla fine ho creato una nuova cartella "Servizi" sotto AdminBundle e ho inserito una nuova classe "CommandKernal". Ha funzionato come un fascino. Grazie mille! – lewsid

Problemi correlati