How to handle custom sql functions on schema update? [closed] - symfony

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
Sometimes when you're looking for performance, you need to delegate some responsibilities to the database with some kind of complexity functions and triggers. I'd like to know what is the best practice to handle those custom sql functions to create/update when doctrine:schema:update command is called.

The easier solution you have (I think) is to create your own command, do your logic inside, and call the doctrine:schema:update at end.
To do this, you can extend your command from the the UpdateSchemaDoctrineCommand or use a Process in your command.
I prefer the first solution, also I will show you.
Create the command in src/Acme/AppBundle/Command/CustomUpdateSchemaDoctrineCommand.php
(for example, use one of your own bundles)
Then, extend it from the parent command like this :
<?php
namespace Acme\AppBundle\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
class CustomUpdateSchemaDoctrineCommand extends UpdateSchemaDoctrineCommand
{
protected function configure()
{
parent::configure();
$this->setName('custom:schema:update')
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// Do your logic
// Update your database schema
return parent::execute($input, $output);
}
}
If you need a tool that allow you to run SQL migrations, use the DoctrineMigrationsBundle

I dont know way to realize automatically running other SQL-code, but you can use DoctrineMigration. It's run manualy, but you can write custom SQL and control versions - file contains creation date, used migrations names will be stored in DB.

Related

What is the symfony way to create an entry in db while migrating?

I have a DB created via migrations that is empty while I would like it to be filled.
Let's say I have a table called 'books' with 'title' and 'author'. What I would like to make sure that at least "knitting with dog hair" by "Kendall Crolius" is an entry in the DB.
I know how to do this in SQL and of course, I could simply put a script in my build process, that executes a command to check if such an entry exists and if not create it.
But what is the Symfony way to create an entry in DB while migrating/setup?
I would not recommend to use fixtures as they are used for test and dev environment, the library should be in the require-dev section in composer.json.
You should create your entities with migration
Use a migration with SQL queries (INSERT).
Don't add entities by entity manager, because your entity can be changed (for example: added required field and after that the migration will be executed with an error)
What you are looking for are data fixtures. I think the two most popular options are:
doctrine/doctrine-fixtures-bundle
hautelook/alice-bundle
With the former you create fixtures in code like this (taken from docs):
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
// create 20 products! Bam!
for ($i = 0; $i < 20; $i++) {
$product = new Product();
$product->setName('product '.$i);
$product->setPrice(mt_rand(10, 100));
$manager->persist($product);
}
$manager->flush();
}
}
You can run them using bin/console doctrine:fixtures:load.
Whereas the latter uses YAML:
App\Entity\Dummy:
dummy_{1..10}:
name: <name()>
related_dummy: '#related_dummy*'
The command for adding the fixtures is: bin/console hautelook:fixtures:load
The AliceBundle has the additional benefit that it comes with integration for Faker, which lets you generate randomized data as well as static data like you suggested.
edit: The main reason for using fixture files is that you want to be independent from the underlying database. When you use an SQL script you might have to keep multiple versions for each supported DB and sometimes even need to adjust it for different versions (e.g. MySQL 5.7 vs. MySQL 8). An additional benefit is, that changes are usually easier to track in a version history in these files than in SQL, but that is debatable. Since Doctrine Fixtures are written in code you can use inspection tools to check if they are in sync with your entities, making it easier to spot when you need to change them.
That being said, if none of those benefits are an argument and you want to use an SQL script then you should keep schema changes and inserting data separate to prevent any issues. Arguably you should also not use migrations for inserting data. You either run the risk of failing the migration because an ID is already used or not rely on IDs and make it difficult to manage relations between data. You could avoid problems by doing INSERT IGNORE but then you will have a hard time identifying if your data is in the state you expect and run the risk of inconsistencies.
I think what you are looking for are fixtures : https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
You just have to write a file with your fixtures (specifying Kendall Crolius etc.) and run this command at the end of the migration process :
php bin/console doctrine:fixtures:load
I think the right way to do this is to create a Symfony console command to migrate your data after you have migrated the schema.
I too missed the Laravel seeders when I moved over to Symfony. I came to realize however that there is nothing that a Laravel seeder can do which cannot be done in a Symfony command.
Migrating the schema and filling the table with data are always two separate operations. In Laravel the seeders are run separately from the migration. I've tried migrating data and schema together and it seems that path always led to tears.
Why is that? I think it is because schema is simple relative to data. Take your gnarliest production database schema and you can quickly identify the changes you need to accomplish whatever you are trying to accomplish. But now you've migrated the schema and it is time to migrate 10+ years of production data into the new order. Perhaps your data slides right in, but mine seems to always be a rat's nest of horrors that requires a lot of special effort. My experience is that I might do and undo the schema migration a few times to get it right. The data migration is something I find that I will do perhaps 10 times because one special case or another has been overlooked.
So it makes sense to build a command specifically to migrate the data for your new schema. This is quite easy in Symfony. See https://symfony.com/doc/current/console.html#creating-a-command to get started.
Briefly the approach is like so. First create a command template
$ symfony console make:command MigrateMyDataCommand
Update your template...
class MigrateMyDataCommand extends Command
{
protected static $defaultName = 'app:mydata:migrate';
protected static $defaultDescription = 'Migrate production data to new schema';
private EntityManagerInterface $em;
//...
// construct to autowire the ORM classes
private EntityManagerInterface $em;
private int $maxnew;
private int $offset;
public function __construct(EntityManagerInterface $em) {
parent::__construct();
$this->em = $em;
}
// add arguments to the command
protected function configure(): void
{
$this->addArgument('offset', InputArgument::OPTIONAL, 'offset to start creating new data');
$this->addArgument('maxnew', InputArgument::OPTIONAL, 'maximum new records created.');
}
//...
// execute the command
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$offset = intval($input->getArgument('offset'));
$maxnew = intval($input->getArgument('maxnew'));
$io->text('Starting Migrate My Data Command');
try {
$this->em->beginTransaction();
//...
$this->em->commit();
} catch(\Exception $ex) {
$this->em->rollback();
$io->error('Migration failed');
return Command::FAILURE;
}
$io->success('My data migration successful.');
return Command::SUCCESS;
}
Run your command...
$ symfony console app:mydata:migrate 0 1
To my mind this is the proper approach. The data fixtures are perfectly fine for testing where you have a bunch of data you can sling around and not care what happens to it. Using SQL INSERT is also fine as long as what you are doing is simple. But if 10,000 users are counting on you to protect their priceless data then I think investing in a specialized command to migrate is the right thing to do.

Symfony 4 - Is it possible to make variable command name?

I am making test CLI-app with symfony 4 and I am wondering, is it possible to make single ClassCommand for multiple purposes?!
For example my test cli-app has several actions/commands but they are very similar. I don't like to duplicate code (also DRY-recommendations did nobody discard) so I thought it would be better to have single command, but depending on it's name choose, which algorithm to use. E.g. command name user:manager will execute one set of instructions and user:designer - another, and all it in one ClassCommand.
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class UserCommand extends Command
{
// some properties
protected function configure()
{
$this
->setName('user:somebody')
// and other stuff
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// output code here
}
}
For other commands for now I have to create another ClassCommand. Ok, I can extend it from UserCommand, but I still have to specify methods "configure" and "execute" (if it has to be overridden). Too many excess code, isn't it?
At the moment I have no idea how to make what I want.
Returning to the question: is it possible to make command-name dynamic?
No that is not possible. But I don't think that would be the correct solution.
I would suggest one of the following approaches; either:
create one command per set of instructions (e.g. one command class for user:manager and one for user:designer). You then use traits or extend from a common abstract class to do the work.
or pass an argument to a single command class - e.g. bin/console user:do-thing --type=designer
I can extend it from UserCommand, but I still have to specify methods "configure" and "execute" (if it has to be overridden). Too many excess code, isn't it?
You could specify both of those methods in your abstract UserCommand, and declare some abstract methods like getCommandName() which you would implement in your designer class.

Own command for loading fixtures-data

There is a doctrine:fixture:load command that will load into my database certain fake data (a previously created fixture class).
I want to create my own command (Console Commands), which will only download selected classes with fake data. In the documentation everything is clear, but I still can not understand what kind of logic should be that would download the selected fake data.
protected function configure()
{
$this
// command name
->setName('app:download:fixture');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->write('There must be logic, but I do not have the faintest idea what it should be');
}
I do not ask to do the job for me, but I ask you to share information on this topic or any advice.
Thank you !
This is what are you looking for: How to call commands in command.
Unfortunately in a new version of doctrine fixtures is not possible to specify which fixtures to load. Before you could do: bin/console doctrine:fixtures:load --fixtures=path/to/fixture.
There are some options what you can do here:
Use old version of doctrine fixtures that has --fixtures option.
You can make a PR or wait until sombody makes it.
Fixtures command looks for all services with tag doctrine.fixture.orm. You can play with compiler passes to dynamically
load them depending on your needs. Don't have idea how to it technically.
Make a command that loads your data by hand.

ASP.Net MVC case conventions [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
I want to know which is the best convention for controller, action and view naming in ASP.Net MVC
When i create a "Product" Controller, should i name it productController or ProductController ?
By default, visual studio Create ProductController.
But that means the url will be http://mywebsite/Product/xxxx
Or we should not have upper case like this in URLs ?
So i do not know which convention apply.
This is the same for view names and action names...
you can use First latter capital as default MVC does, and for lower case url you can use RouteCollection.LowercaseUrls Property
check below link
https://msdn.microsoft.com/en-us/library/system.web.routing.routecollection.lowercaseurls.aspx
Example:
public static void RegisterRoutes(RouteCollection routes)
{
routes.LowercaseUrls = true;
}
Controller is just a class and should follow class capitalization rules, iow should use PascalCase: https://msdn.microsoft.com/en-us/library/x2dbyw72(v=vs.71).aspx
You can use StyleCop to check your code for such things as casing rules, spaces, etc: https://stylecop.codeplex.com/
But still nobody can prevent you from naming like 'productController'.
Also, if you wish to change appearance of controller name in URL, please check next question and corresponding answers (but usually people ten to use default naming): How to Change ASP.NET MVC Controller Name in URL?
In short, you can do this with RoutePrefixAttribute or by configuring routing (not shown here):
[RoutePrefix("product")]
public class ProductController
{
...
}

Loading Fixtures for functional tests in Symfony 2: Call to undefined method MyControllerTest::loadFixtures())

I am trying to find the easy way to load my fixtures in Symfony 2.6 to run functional tests. This is a quite common question, and has been asked a few times, but the answers I have found so far do not quite reach my expectations:
Some rely on running the command line from inside the functional test.
Other run manually each one of the defined fixtures, and then take care of creating and deleting the database.
There is a lot of overhead in both cases (use statements and manual code), for a task that I believe is very standard.
On the other hand, these same posts recommend the LiipFunctionalTestBundle. Going for it, here is what I read in the installation instructions:
write fixture classes and call loadFixtures() method from the bundled
Test\WebTestCase class. Please note that loadFixtures() will delete the contents from the database before loading the fixtures."
So I tried...
namespace AppBundle\Test\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MyControllerTest extends WebTestCase
{
public function setUp()
{
$classes = array(
'AppBundle\DataFixtures\LoadUserData',
);
$this->loadFixtures($classes);
}
...
}
With no luck:
Call to undefined method AppBundle\Tests\Controller\MyControllerTest::loadFixtures() in /gitrepos/myproject/src/AppBundle/Tests/Controller/MyControllerTest.php on line 15
a static call gives the same error...
self:loadFixtures($classes);
I really think I am missing something pretty obvious. Anyone can get me back on track ?
I can see you're using
Oro\Bundle\TestFrameworkBundle\Test\WebTestCase
as the base class while I think you should use
Liip\FunctionalTestBundle\Test\WebTestCase
to be able to call this method.

Resources