Fixtures + Migrations Doctrine, Symfony - symfony

I want to create fixture, that generate 10 records in my table (test0-test9), then create migration, wherein I need to rename records, that was created by fixture to (category0-category9).
I have created this fixture:
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
for ($i = 0; $i < 10; $i++)
{
$product = new Category();
$product->setName('test '.$i);
$id = mt_rand(89,140);
$parent = $manager->getRepository(Category::class)->find($id);
$product->setParent($parent);
$manager->persist($product);
}
$manager->flush();
}
}
But how I can rename this records in doctrine using migration? Any idea?
* I think, I need to create sql queries directly in my migration class...or not
UPDATE
I try to do this, but I think it is bad solution...
public function postUp(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
for ($i = 0; $i < 10; $i++)
{
$category = 'category '.$i;
$test = 'test'.$i;
$this->addSql('UPDATE category SET NAME = '.$category.' WHERE NAME = '.$test );
}
}

You can inject the entity manager inside your migration.
Then you can find the object you want to change and change it.
I hope this answers your question
Example:
public function postUp(Schema $schema)
{
$em = $this->container->get('doctrine.orm.entity_manager');
$products = $em->getRepository(Product::class)
->getProductsCustomQuery();
foreach($product as $products){
$product->setName(Whatever);
}
$em->flush();
}
Check this link

Related

OpenApiFactory 2.6.x decorating context

I passed on 2.6.x ApiPlatform recently and I have a SwaggerDecorator service to generate some custom documentation.
As I could see now we have to decorate api_platform.openapi.factory in order to customize our documentation.
Problem : as before it was a normalizer, we had the format and context passed to the custom decorator :
public function normalize($object, $format = null, array $context = []): array
and each normalize/denormalize context was perform to generate the documentation.
I try to find a way to reproduce that with decorating the new OpenAPiFactory
Questions :
how can I get the format now ?
how can I get my custom documentation working for all output context ?
relative documentation links just have a single example :
[https://api-platform.com/docs/core/openapi/]
actual stack
PHP 7.4.8
Symfony 5.2.x
ApiPlatform 2.6.x
actual try code :
public function __invoke(array $context = []): OpenApi
{
$result = $this->cache->get('openapi_documentation', function (ItemInterface $item) use ($context) {
// Build default doc
$this->docs = $this->decorated->__invoke($context);
if (!$this->docs instanceof OpenApi) {
return $this->docs;
}
// Complete API docudmentation if files exist in schemas configuration directory
$schemas = $this->docs->getComponents()->getSchemas();
foreach ($schemas ?? [] as $k => &$schema) {
$schemaNameExploded = explode(':', $k);
$objectName = strstr($schemaNameExploded[0], '-', true) ?: $schemaNameExploded[0];
$docFile = $this->confDir.'/packages/api_platform/schemas/'.$objectName.'.yaml';
$this->completeDoc($schema, $docFile);
}
});
return $result;
}
/**
* Complete the openapi documentation of $schema with $docFile.
*
* #param array|\ArrayObject $schema
*
* #return mixed
*/
private function completeDoc(&$schema, string $docFile)
{
if (!file_exists($docFile)) {
return $schema;
} else {
$docFileParsed = (array) Yaml::parseFile($docFile);
}
// Treatment of the description
if (isset($docFileParsed['description'])) {
$schema['description'] = $docFileParsed['description'];
}
// Treatment of properties
if (isset($schema['properties'])) {
$newProperties = $docFileParsed['properties'] ?? [];
foreach ($schema['properties'] as $k => &$currentProperty) {
if (!empty($newProperties[$k])) {
$currentPropertyArray = $currentProperty instanceof \ArrayObject ? $currentProperty->getArrayCopy() : $currentProperty;
$currentProperty = new \ArrayObject(array_replace($currentPropertyArray, $newProperties[$k]));
}
}
}
}
thx

Symfony/FOSUserBundle: Load an existing User from database in Fixtures

My problem comes from the fact that i have a relation in my entities that depends on each other. My entity Link has a relation with an entity User. When I try to load the User using UserManagerInterface it returns NULL.
Here is My AppFixtures class:
<?php
namespace App\DataFixtures;
use App\Component\Localization;
use App\Entity\Link;
use App\Entity\LinkClick;
use App\Entity\LinkView;
use App\Entity\Visitor;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker;
use FOS\UserBundle\Model\UserManagerInterface;
use PUGX\Shortid\Shortid;
class AppFixtures extends Fixture
{
private $userManager;
private $localisation;
public function __construct(UserManagerInterface $userManager, Localization $localization)
{
$this->userManager = $userManager;
$faker = Faker\Factory::create();
$localization->setParameters($localization::GEO_PLUGIN, $faker->ipv4);
$localization->localize();
$this->localisation = $localization->getResult();
}
public function load(ObjectManager $manager): void
{
$faker = Faker\Factory::create();
$user = $this->userManager->createUser();
$user->setUsername('admin');
$user->setEmail('admin#kss.tk');
$user->setPlainPassword('admin');
$user->setEnabled(true);
$user->setRoles(['ROLE_ADMIN']);
$this->userManager->updateUser($user);
$links = [];
$visitors = [];
for ($i = 0; $i < 100; ++$i) {
$link = new Link();
$link->setUrl($faker->url);
$link->setSlug(Shortid::generate(12, 'abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01', true));
$link->setCreatedAt($faker->dateTime);
$link->setUser($user);
$links[] = $link;
$manager->persist($link);
}
for ($i = 0; $i < 10000; ++$i) {
$visitor = new Visitor();
$visitor->setIp($this->localisation->geoplugin_request);
$visitor->setCountryCode($this->localisation->geoplugin_countryCode);
$visitor->setCountryName($this->localisation->geoplugin_countryName);
$visitors[] = $visitor;
$manager->persist($visitor);
}
for ($i = 0; $i < 10000; ++$i) {
$linkView = new LinkView();
$linkView->setDate($faker->dateTimeBetween('-1 years', 'now'));
$linkView->setVisitor($visitors[rand(0, 999)]);
$linkView->setLink($links[rand(0, 99)]);
$manager->persist($linkView);
}
for ($i = 0; $i < 10000; ++$i) {
$linkClick = new LinkClick();
$linkClick->setDate($faker->dateTimeBetween('-1 years', 'now'));
$linkClick->setVisitor($visitors[rand(0, 999)]);
$linkClick->setLink($links[rand(0, 99)]);
$manager->persist($linkClick);
}
$manager->flush();
}
}
I dont want to flush the database everytime, i want to use and existing user, so i can append fixtures. Right now it throws an error:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'admin' for key 'UNIQ_1483A5E992FC23A8'

Symfony Fixtures - Get Random Entity Instance

I am trying to load fixtures into my database. I have a post entity and a category entity, and a category can have many posts.
In my fixtures file I would like to create some categories and then assign a random category to each post, but I am not sure how to do this.
How can I get a reference to a random category?
<?php
namespace App\DataFixtures;
use App\Entity\Category;
use App\Entity\User;
use App\Entity\Post;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use Faker\Factory;
use Faker\Generator;
class AppFixtures extends Fixture
{
private $faker;
public function load(ObjectManager $manager)
{
$this->faker = Factory::create();
$this->addUsers($manager);
$this->addCategories($manager);
$this->addPosts($manager);
$manager->flush();
}
private function addUsers(EntityManager $em)
{
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$firstname = $this->faker->firstName;
$lastname = $this->faker->lastName;
$user->setFirstName($firstname);
$user->setLastName($lastname);
$user->setEmail($firstname.'.'.$lastname.'#gmail.com');
$user->setRoles(['ROLE_USER']);
$em->persist($user);
$this->addPosts($user);
}
}
private function addCategories(EntityManager $em)
{
$categoryHome = new Category();
$categoryHome->setName('Home');
$em->persist($categoryHome);
$categoryWork = new Category();
$categoryWork->setName('Work');
$em->persist($categoryWork);
}
public function addPosts(EntityManager $em, User $user){
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
// How can I assign the category randomly?
$post->setCaterory(????)
$em->persist($post);
}
}
}
If you need to create random data (for example for presentation) I think better choice would be using AliceBundle https://github.com/hautelook/AliceBundle which can do this out of box via relations. Plus it will be same on every load of fixtures.
But to answer your question. You make list of categories you created and then just choose one randomly.
<?php
namespace App\DataFixtures;
use App\Entity\Category;
use App\Entity\User;
use App\Entity\Post;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManager;
use Faker\Factory;
use Faker\Generator;
class AppFixtures extends Fixture
{
private $faker;
private $categories;
public function load(ObjectManager $manager)
{
$this->faker = Factory::create();
$this->addUsers($manager);
$this->addCategories($manager);
$this->addPosts($manager);
$manager->flush();
}
private function addUsers(EntityManager $em)
{
for ($i = 1; $i <= 10; $i++) {
$user = new User();
$firstname = $this->faker->firstName;
$lastname = $this->faker->lastName;
$user->setFirstName($firstname);
$user->setLastName($lastname);
$user->setEmail($firstname.'.'.$lastname.'#gmail.com');
$user->setRoles(['ROLE_USER']);
$em->persist($user);
$this->addPosts($user);
}
}
private function addCategories(EntityManager $em)
{
$categoryHome = new Category();
$categoryHome->setName('Home');
$em->persist($categoryHome);
$this->categories[] = $categoryHome;
$categoryWork = new Category();
$categoryWork->setName('Work');
$em->persist($categoryWork);
$this->categories[] = $categoryWork;
}
public function addPosts(EntityManager $em, User $user){
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
$post->setCaterory($this->categories[rand(0, count($this->categories))]);
$em->persist($post);
}
}
}
In AddPost Function Add this
public function addPosts(EntityManager $em, User $user){
$categories = $manager->getRepository(Category::class)->findAll();
for ($i = 1; $i <= 10; $i++) {
$post = new Post();
$post->setUser($user);
$category = $categories[array_rand($categories)];
$post->setCategory($category)
$em->persist($post);
}
}
}

How to implement a nice solution for multilang entity slug based routes in Symfony2

I'd like to create a simple bundle to handle some multilingual pages in a website with translated slugs.
Based on translatable, sluggable and i18nrouting
implemented an entity (Page) with title, content, slug fields + locale property as the doc says
created a new Page set its title and content then translated it by $page->setTranslatableLocale('de'); and set those fields again with the german values, so that the data in the tables looks fine, they are all there
implemented the controller with type hinting signature: public function showAction(Page $page)
generated some urls in the template by: {{ path("page_show", {"slug": "test", "_locale": "en"}) }} and {{ path("page_show", {"slug": "test-de", "_locale": "de"}) }}, routes are generated fine, they look correct (/en/test and /de/test-de)
clicking on them:
Only the "en" translation works, the "de" one fails:
MyBundle\Entity\Page object not found.
How to tell Symfony or the Doctrine or whatever bundle to use the current locale when retrieving the Page? Do I have to create a ParamConverter then put a custom DQL into it the do the job manually?
Thanks!
Just found another solution which I think is much nicer and i'm going to use that one!
Implemented a repository method and use that in the controller's annotation:
#ParamConverter("page", class="MyBundle:Page", options={"repository_method" = "findTranslatedOneBy"})
public function findTranslatedOneBy(array $criteria, array $orderBy = null)
{
$page = $this->findOneBy($criteria, $orderBy);
if (!is_null($page)) {
return $page;
}
$qb = $this->getEntityManager()
->getRepository('Gedmo\Translatable\Entity\Translation')
->createQueryBuilder('t');
$i = 0;
foreach ($criteria as $name => $value) {
$qb->orWhere('t.field = :n'. $i .' AND t.content = :v'. $i);
$qb->setParameter('n'. $i, $name);
$qb->setParameter('v'. $i, $value);
$i++;
}
/** #var \Gedmo\Translatable\Entity\Translation[] $trs */
$trs = $qb->groupBy('t.locale', 't.foreignKey')->getQuery()->getResult();
return count($trs) == count($criteria) ? $this->find($trs[0]->getForeignKey()) : null;
}
It has one disadvantage there is no protection against same translated values ...
I found out a solution which i'm not sure the best, but works.
Implemented a PageParamConverter:
class PageParamConverter extends DoctrineParamConverter
{
const PAGE_CLASS = 'MyBundle:Page';
public function apply(Request $request, ParamConverter $configuration)
{
try {
return parent::apply($request, $configuration);
} catch (NotFoundHttpException $e) {
$slug = $request->get('slug');
$name = $configuration->getName();
$class = $configuration->getClass();
$em = $this->registry->getManagerForClass($class);
/** #var \Gedmo\Translatable\Entity\Translation $tr */
$tr = $em->getRepository('Gedmo\Translatable\Entity\Translation')
->findOneBy(['content' => $slug, 'field' => 'slug']);
if (is_null($tr)) {
throw new NotFoundHttpException(sprintf('%s object not found.', $class));
}
$page = $em->find($class, $tr->getForeignKey());
$request->attributes->set($name, $page);
}
return true;
}
public function supports(ParamConverter $configuration)
{
$name = $configuration->getName();
$class = $configuration->getClass();
return parent::supports($configuration) && $class == self::PAGE_CLASS;
}
}
TranslationWalker nicely gets the entity in active locale:
class PagesRepository extends \Doctrine\ORM\EntityRepository
{
public function findTranslatedBySlug(string $slug)
{
$queryBuilder = $this->createQueryBuilder("p");
$queryBuilder
->where("p.slug = :slug")
->setParameter('slug', $slug)
;
$query = $queryBuilder->getQuery();
$query->setHint(
Query::HINT_CUSTOM_OUTPUT_WALKER,
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
);
return $query->getSingleResult();
}
}
And in controller
/**
* #Entity("page", expr="repository.findTranslatedBySlug(slug)")
* #param $page
*
* #return Response
*/
public function slug(Pages $page)
{
// thanks to #Entity annotation (Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity)
// Pages entity is automatically retrieved by slug
return $this->render('content/index.html.twig', [
'page' => $page
]);
}

Symfony2 how to get entity alias in repository

i would like to write some DQL query in my entity repository function but instead of haroding entity alias into DQL i would like to get actual entity alias from repository.
my repository:
/**
* TrackingRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class TrackingRepository extends EntityRepository
{
public function test()
{
$dql = 'SELECT * FROM MyBundle:Tracking';
// i would like to call something like this:
// $dql = 'SELECT * FROM ' . $this->getEntityAlias;
$query = $this->getEntityManager()->createQuery($dql);
...
}
}
is this somehow possible?
You can get the entity class with $this->getClassName() in your repository :
class TrackingRepository extends EntityRepository
{
public function test()
{
$dql = 'SELECT t FROM ' . $this->getClassName() . ' t';
$query = $this->getEntityManager()->createQuery($dql);
...
}
}
class TrackingRepository extends EntityRepository
{
public function test()
{
$dql = 'SELECT t.property1,t.property2,t.property3,t.property4 FROM MyBundle:Tracking t';
// i would like to call something like this:
// $dql = 'SELECT * FROM ' . $this->getEntityAlias;
$query = $this->getEntityManager()->createQuery($dql);
...
}
}
What kind of query you want to execute? Are you realy need DQL? There are other ways to achive execute complex query, please consider:
Repository findBy($criteria):
public function test()
{
$this-> findBy($criteria);
}
For more complex queries you can also use:
Criteria and matching:
use Doctrine\Common\Collections\Criteria;
//
public function test()
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq('sth', 'val'))
// more criteria here
$result = $this->matching($criteria);
}
Doctrine's Query Builder
Or even Query Builder with specific criteria expressions:
public function test()
{
$qb = $er->createQueryBuilder('p');
$qb
->where($qb->expr()->andx(
$qb->expr()->in('p', '?1'),
$qb->expr()->isNotNull('p.someField')
))
->setParameter(1, $someValue);
$result = $this->matching($criteria);
}

Resources