Reputation: 377
I'm working on a Symfony 4.2 project and I'm looking for the best practice to achieve a reset of the database when the admin needs to do it via a button in backoffice.
Explanation :
The project is a temporary event website. This means that, people will visit the website only for a day / a week and then the website is off. Per example, a website for spectators into a stadium during a basketball tournament.
When the tournament is over, the administrator would like to reset all datas sent during it via a button.
Right now I did it like this but I don't know if it's the better way in production environment.
I created a service which get the KernelInterface in constructor :
public function resetDB() {
$application = new Application($this->kernel);
$application->setAutoExit(false);
$input = new ArrayInput([
'command' => 'doctrine:schema:drop',
'--force' => true
]);
$output = new BufferedOutput();
$application->run($input, $output);
$responseDrop = $output->fetch();
if (strpos($responseDrop, 'successfully') !== false) {
$input = new ArrayInput([
'command' => 'doctrine:schema:create',
]);
$application->run($input, $output);
$responseCreate = $output->fetch();
if (strpos($responseCreate, 'successfully') !== false)
return new Response();
}
return new \ErrorException();
}
Firstly, is it good to do it like this in a production environment ? (Nobody else the administrator will use the website when doing this operation)
Secondly, I'm not really satisfied with the method I used to check if the operation has been successfully done (strpos($responseCreate, 'successfully') !== false
). Does someone know a better way ?
Thanks a lot for your help
Upvotes: 4
Views: 5387
Reputation: 12730
Firstly, is it good to do it like this in a production environment ?
I don't think so! For example the commands below would warn you with: [CAUTION] This operation should not be executed in a production environment!
. However, everything is possible in wild programming world as shown below.
Try Symfony's The Process Component.
This is the basic example so it is up to you make it cleaner and duplication free. I tested and it works. You can stream the output as well.
# DROP IT
$process = new Process(
['/absolute/path/to/project/bin/console', 'doctrine:schema:drop', '--force', '--no-interaction']
);
$process->run();
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
# RECREATE IT
$process = new Process(
['/absolute/path/to/project/bin/console', 'doctrine:schema:update', '--force', '--no-interaction']
);
$process->run();
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
Upvotes: 1
Reputation: 10877
I'm not sure about the way you executing the commands, but there is a single command alternative to consider, using DoctrineFixturesBundle. You need to install it for use in the production environment (technically not recommended, I think because of risk of deleting prod data, but that's what you want to do).
Install:
$ composer require doctrine/doctrine-fixtures-bundle
Config:
// config/bundles.php
return [
...
Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['all' => true],
...
];
You do need to create a fixture and it must have a load()
method that is compatible with Doctrine\Common\DataFixtures\FixtureInterface::load(Doctrine\Common\Persistence\ObjectManager $manager), but it can be empty exactly like below:
<?php // src/DataFixtures/AppFixtures.php
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager){}
}
The command:
$ php bin/console doctrine:fixtures:load -n --purge-with-truncate --env=prod
Help:
$ php bin/console doctrine:fixtures:load --help
Upvotes: 1
Reputation: 17566
If it works for you, its ok. About the "successful" check part. Simply surround your call in a try-catch block and check for exceptions. If no exception was thrown, assume it did execute successfully.
$application = new Application($this->kernel);
$application->setAutoExit(false);
try {
$application->run(
new StringInput('doctrine:schema:drop --force'),
new DummyOutput()
);
$application->run(
new StringInput('doctrine:schema:create'),
new DummyOutput()
);
return new Response();
} catch (\Exception $exception) {
// don't throw exceptions, use proper responses
// or do whatever you want
return new Response('', Response::HTTP_INTERNAL_SERVER_ERROR);
}
Is PostgreSQL good enough at DDL transactions? Force a transaction then:
$application = new Application($this->kernel);
$application->setAutoExit(false);
// in case of any SQL error
// an exception will be thrown
$this->entityManager->transactional(function () use ($application) {
$application->run(
new StringInput('doctrine:schema:drop --force'),
new DummyOutput()
);
$application->run(
new StringInput('doctrine:schema:create'),
new DummyOutput()
);
});
return new Response();
Upvotes: 2