I have troube with symfony fixtures - symfony

I discovered the symfony framework for a short time. and there, I'm stuck at the fixture level. I managed to create category fixtures, but I can't manage with the product fixtures. If anyone can help, that would be nice.
namespace App\DataFixtures;
use App\Entity\Category;
use App\Entity\Product;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
for ( $i = 1 ; $i <= 10 ; $i++){
$category = new Category();
$category ->setName("category".$i);
$category ->setImage("road to pic".$i);
$manager ->persist($category);
}
$manager ->flush($category);
$productRepo = $manager->getRepository(Product::class);
$allCategory = $productRepo->findAll();
for ( $i = 1 ; $i <= 200 ; $i++){
$product = new Product();
$product ->setName("product".$i);
$product ->setImage("road to pic".$i);
$product ->setPrice(rand(1, 100));
$product ->setStock(rand(1,10));
$product ->setDescription("description".$i);
$product ->setCategory($allCategory[1]);
$manager -> persist($product);
}
$manager->flush($product);
}
}

You are getting the list of categories incorrectly in your example.
Instead of this
$productRepo = $manager->getRepository(Product::class);
$allCategory = $productRepo->findAll();
Use it
$allCategory = $manager->getRepository(Category::class)->findAll();

Related

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);
}
}
}

Fixtures + Migrations Doctrine, 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

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
]);
}

PHP and Symfony object to array conversion by type cast

I'm using symfony2 and createForm to get the http post data. After witch I do:
$Data = (array) $form->getData();
And I get:
array (size=1)
'�Far\MT\AccountBundle\Entity\Movement�toAccount' => int 3
I don't think this is the normal behavior for these cases, any sugestions?
the toAccount should be the complete index name.
Wasn't able to reproduce the conditions in a test case for the cli:
<?php
namespace A;
class MyClass
{
public $id;
public $name;
public $age;
}
$object = new MyClass();
$object->name = "Andre";
$object->id = 1;
$object->age = 30;
var_dump($object);
$Ar = (array) $object;
var_dump($Ar)
This above worked ok.
I used this solution:
//comment
$Data = $form->getData();
$obj = new \ReflectionObject($Data);
$props = $obj->getProperties();
$propname = array();
foreach ($props as $prop) {
$tmp = "get".ucfirst($prop->name);
if (($res = $Data->$tmp() )!== null) {
$propname[$prop->name] = $res;
}
}
$tmpSearch = $propname;
I'll clean it up after.
You can use the Symfony normalizer class, as your propose will fail when you have fields in your form name with underscore like 'facility_id' but your setter is called facilityId
<?php
$data = $form->getData();
$normalizers = new \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer();
$norm = $normalizers->normalize($data);
print_r($norm);
you'll get output like
Array ( [fullname] => fullnameVal [email] => emaile#lkjl.com [phoneNumber] => 5554444 [facilityId] => 123132 )

Extend wpdb in other class - when use get_results for select gives me null

I've added custom plugin (created by me) in WP in that plugin I have Class named BaseModel, which extends wpdb.
The problem here is everytime when I try to run query I get false or null or empty array as result.
class BaseModel extends wpdb{
public function __construct(){
parent::__construct(DB_HOST, DB_USER, DB_PASS, DB_NAME);
}
function get_destinations($limit, $order){
$query = "SELECT * FROM wp_relations";
$result = $this->get_results($query, ARRAY_A);
var_dump($result);
}
function get_total_destinations(){
}}
Can some one tell me what is wrong?
Thanks.
Actually it is not a full OOP solution but I solve this by adding global $wpdb into my functions.
class BaseModel {
function get_destinations($limit, $order){
global $wpdb;
$query = "SELECT * FROM wp_relations";
$result = $wpdb->get_results($query, ARRAY_A);
var_dump($result);
}
function get_total_destinations(){
}}
I hope you will find this helpful.
More Info WordPress tests with wpdb
<?php
class testWPDB extends wpdb {
function prepare( $query, $arguments ){
return vsprintf( $query, $arguments );
}
}
class UTCW_Test_Data extends WP_UnitTestCase {
protected $utcw;
function setUp(){
$this->utcw = UTCW_Plugin::get_instance();
}
function getWPDBMock(){
return $this->getMock( 'testWPDB', array( 'get_results' ), array(), '', false );
}
function test_author(){
$instance[ 'authors' ] = array( 1, 2, 3 );
$config = new UTCW_Config( $instance, $this->utcw );
$db = $this->getWPDBMock( 'get_results' );
$db->expects( $this->once() )
->method( 'get_results' )
->with( $this->stringContains( 'post_author IN (1,2,3)' ) );
$data = new UTCW_Data( $config, $db );
$data->get_terms();
}
}
I dont think you want to extend from it? If this class will always be loaded inside Wordpress files then you will have access to the global $wpdb.
class RandomClass {
private $wpdb = false;
public function __construct() {
global $wpdb;
if (is_object($wpdb)) {
$this->wpdb = $wpdb;
}
}
public function get_results($data) {
return $this->wpdb->get_results($data);
}
}

Resources