Sorting joined table with KnpPaginatorBundle - sorting

I set up a test to get more familiar with Symfony2 and KnpPaginatorBundle. I have a table with pets that references the pet animal type (Dog, Cat, Etc). I can sort by the id, name, but when I try to sort by animal type I get an error message stating:
There is no such field [animalkind] in the given Query component, aliased by [a]
I have tried various field names, but nothing seems to work.
Entity: MyPets.php
<?php
namespace Xyz\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* MyPet
*/
class MyPet
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* Set id
*
* #param integer $id
* #return MyPet
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return MyPet
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #var \Xyz\TestBundle\Entity\AnimalKind
*/
private $AnimalKind;
/**
* Set AnimalKind
*
* #param \Xyz\TestBundle\Entity\AnimalKind $animalKind
* #return MyPet
*/
public function setAnimalKind(\Xyz\TestBundle\Entity\AnimalKind $animalKind)
{
$this->AnimalKind = $animalKind;
return $this;
}
/**
* Get AnimalKind
*
* #return \Xyz\TestBundle\Entity\AnimalKind
*/
public function getAnimalKind()
{
return $this->AnimalKind;
}
}
Entity: AnimalKind.php
<?php
namespace Xyz\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* AnimalKind
*/
class AnimalKind {
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $type;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set type
*
* #param string $type
* #return AnimalKind
*/
public function setType($type) {
$this->type = $type;
return $this;
}
/**
* Get type
*
* #return string
*/
public function getType() {
return $this->type;
}
/**
* #var \Doctrine\Common\Collections\Collection
*/
private $MyPets;
/**
* Constructor
*/
public function __construct() {
$this->MyPets = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add MyPets
*
* #param \Xyz\TestBundle\Entity\MyPet $myPets
* #return AnimalKind
*/
public function addMyPet(\Xyz\TestBundle\Entity\MyPet $myPets) {
$this->MyPets[] = $myPets;
return $this;
}
/**
* Remove MyPets
*
* #param \Xyz\TestBundle\Entity\MyPet $myPets
*/
public function removeMyPet(\Xyz\TestBundle\Entity\MyPet $myPets) {
$this->MyPets->removeElement($myPets);
}
/**
* Get MyPets
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMyPets() {
return $this->MyPets;
}
public function __toString() {
return $this->getType();
}
}
Controller: MyPetController.php (IndexAction)
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery("SELECT a FROM XyzTestBundle:MyPet a");
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($query,$this->get('request')->query->get('page',
1)/*page number*/,15/*limit per page*/);
return $this->render('XyzTestBundle:MyPet:index.html.twig', array(
'pagination' => $pagination,
));
}
View: MyPet/index.html.twig (snipped)
<table class="records_list">
<thead>
<tr>
{# sorting of properties based on query components #}
<th>{{ knp_pagination_sortable(pagination, 'Id', 'a.id') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'Name', 'a.name') }}</th>
<th>{{ knp_pagination_sortable(pagination, 'kind', 'a.animalkind') }}</th>
</tr>
</thead>
<tbody>
{% for mypet in pagination %}
<tr>
<td>{{ mypet.id }}</td>
<td>{{ mypet.name }}</td>
<td>{{ mypet.animalkind }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Can anyone give me any insight as to the problem?

The first issue is the missing ORM relationship declaration; basically in your DB YourPet cannot be associated to an AnimalKind.
You can find documentation about Entity relationships and associations on the Symfony's website.
From YourPet entity's point of view, you probably want to use a ManyToOne relationship.
The second issue, once you fix the problem above, is that you are not joining the AnimalKind entity in your query.
The third issue is that you cannot order a query result by an entity.
First issue potential solution:
// In MyPet class
/**
* #var \Xyz\TestBundle\Entity\AnimalKind
* #ORM\OneToMany(targetEntity="AnimalKind", inversedBy="MyPets")
*/
private $AnimalKind;
// In AnimalKind class
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToOne(targetEntity="MyPet", inversedBy="AnimalKind")
*/
private $MyPets;
Second issue potential solution:
$query = $em->createQuery("
SELECT a, k
FROM XyzTestBundle:MyPet a
JOIN a.animalKind k
");
Third issue potential solution:
<th>{{ knp_pagination_sortable(pagination, 'kind', 'k.type') }}</th>

To display animals with no animalKind you'll have to use a LEFT JOIN in your query
$query = $em->createQuery("
SELECT a, k
FROM XyzTestBundle:MyPet a
LEFT JOIN a.animalKind k
");

For those using query builder and DQL and still looking for answer:
$qb = $this->createQueryBuilder('a');
$qb->select('a', 'k')
->leftJoin(
'a.animalKind',
'k'
)
;
Assumption -> you have correctly created relation in your entity.

Related

Store JSON data into TEXT mysql column with doctrine

I have an entity with one TEXT (MySQL) attributes
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Index;
use ApiPlatform\Core\Annotation\ApiProperty;
/**
* #ApiResource(
* attributes={},
* collectionOperations={
* "get"={},
* "post"={
* "access_control"="is_granted('ROLE_COMPANY')"
* },
* },
* itemOperations={
* "get"={},
* "put"={"access_control"="is_granted('ROLE_COMPANY')"},
* }
* )
* #ORM\Entity(
* repositoryClass="App\Repository\SettingRepository",
* )
* #ORM\Table(
* indexes={#Index(name="domain_idx", columns={"domain"})}
* )
*/
class Setting
{
/**
* #var Uuid
* #ApiProperty(identifier=true)
* #ORM\Id
* #ORM\Column(type="string")
* #ORM\GeneratedValue(strategy="NONE")
*/
private $identifier;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $data = array();
/**
* #ORM\Column(type="string", nullable=true)
*/
private $domain = array();
public function getData()
{
if($this->data == null) return array();
$data = unserialize($this->data);
return $data;
}
public function setData($data): self
{
$this->data = serialize($data);
return $this;
}
/**
* #return mixed
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* #param mixed $key
*/
public function setIdentifier($identifier): self
{
$this->identifier = $identifier;
return $this;
}
/**
* #return mixed
*/
public function getDomain()
{
return $this->domain;
}
/**
* #param mixed $domain
*/
public function setDomain($domain): self
{
$this->domain = $domain;
return $this;
}
}
If I try to invoke the service with the following parameter structure it works fine:
{
"data": "testData",
"identifier": "testIdentifier",
"domain": "domain1"
}
But If I would like to store an embedded JSON string, for example:
"data": {"temp": 123}
I receive the following error:
hydra:description": "The type of the \"data\" attribute must be \"string\", \"array\" given.",
I tried to convert the object into an string in the method setData. But this method will not be invoked. It seams, that the API-Platform detects the wrong type and throws the exception.
I found some comments, that it is necessary to decorate the property:
https://api-platform.com/docs/core/serialization/#decorating-a-serializer-and-adding-extra-data
Can anyone give me an example? It does not work!
Where is the right place to serialise and unserialise the property data?
Does anyone have an idea?
Kind regards
You need to set the column type to json in MySQL. It should behave as expected.
/**
* #var array Additional data describing the setting.
* #ORM\Column(type="json", nullable=true)
*/
private $data = null;
I think null is more consistent than an empty array, but that's your choice.

Unable to add select subquery in from clause with ZF2 and doctrine

I am using ZF2 version-2.5.0 and doctrine version- ~2.5
I have three entities namely event, ranking and country as follows :
<?php
namespace SampleProject\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use SampleProject\Entity\Country;
use SampleProject\Entity\Ranking;
class Event
{
/**
*
* #ORM\Id
* #ORM\Column(name="eventID", type="integer", precision=0, nullable=false)
* #var int $eventID
*/
protected $eventID;
/**
* #ORM\Column(name="name", type="string", length=50, precision=0, nullable=false)
* #var string $name
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="SampleProject\Entity\Ranking", mappedBy="event")
* #var ArrayCollection $rankings
*/
protected $rankings;
/**
* #ORM\ManyToOne(targetEntity="SampleProject\Entity\Country", inversedBy="events", fetch="EXTRA_LAZY", cascade={"detach"})
* #ORM\JoinColumn(name="countryIso", referencedColumnName="countryIso", nullable=false)
* #var \SampleProject\Entity\Country $country
*/
protected $country;
/**
* Initializes doctrine collections, called from constructor in entity class
*/
protected function initializeCollections()
{
$this->rankings = new ArrayCollection();
}
public function setEventID($eventID)
{
$this->eventID = (int)$eventID;
return $this;
}
public function getEventID()
{
return $this->eventID;
}
public function setName($name)
{
$this->name = (string)$name;
return $this;
}
public function getName()
{
return $this->name;
}
public function setCountry(Country $country)
{
$this->country = $country;
return $this;
}
public function getCountry()
{
return $this->country;
}
public function addRanking(Ranking $ranking)
{
$this->rankings[] = $ranking;
return $this;
}
public function getRankings()
{
return $this->rankings;
}
}
?>
<?php
namespace SampleProject\Country;
use SampleProject\Entity\Event;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
class Country
{
/**
*
* #ORM\Id
* #ORM\Column(name="countryIso", type="string", length=3, precision=0, nullable=false)
* #var string $countryIso
*/
protected $countryIso;
/**
* #ORM\Column(name="name", type="string", length=50, precision=0, nullable=false)
* #var string $name
*/
protected $name;
/**
* #ORM\OneToMany(targetEntity="SampleProject\Entity\Event", mappedBy="country", fetch="EXTRA_LAZY")
* #var ArrayCollection $events
*/
protected $events;
/**
* Initializes doctrine collections, called from constructor in entity class
*/
protected function initializeCollections()
{
$this->events = new ArrayCollection();
}
public function setCountryIso($countryIso)
{
$this->countryIso = (string) $countryIso;
return $this;
}
public function getCountryIso()
{
return $this->countryIso;
}
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function addEvent(Event $event)
{
$this->events->add($event);
return $this;
}
public function getEvents()
{
return $this->events;
}
}
?>
<?php
namespace SampleProject\Ranking;
use Doctrine\ORM\Mapping as ORM;
use SampleProject\Entity\Event;
class Ranking
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Column(type="integer", precision=0, nullable=false)
* #var int $rankingID
*/
protected $rankingID;
/**
* #ORM\Column(name="rank", type="integer", precision=0, nullable=true)
* #var int $rank
*/
protected $rank;
/**
* #ORM\ManyToOne(targetEntity="SampleProject\Entity\Event", inversedBy="rankings", fetch="LAZY")
* #ORM\JoinColumn(name="eventID", referencedColumnName="eventID", nullable=false)
* #var \SampleProject\Entity\Event $event
*/
protected $event;
/**
* #ORM\Column(name="locale", type="string", length=5, precision=0, nullable=true)
* #var string $locale
*/
protected $locale;
public function getRankingID()
{
return $this->rankingID;
}
public function setRank($rank)
{
$this->rank = (int) $rank;
return $this;
}
public function getRank()
{
return $this->rank;
}
public function setEvent(Event $event)
{
$this->event = $event;
return $this;
}
public function getEvent()
{
return $this->event;
}
public function getLocale()
{
return $this->locale;
}
public function setLocale($locale)
{
$this->locale = $locale;
}
}
?>
One event belongs to one country and one country can have multiple events.
One event have multiple rankings depending on locale i.e. (en, nl etc.).
I am using ZF2 and doctrine to get listing of events order by rankings in ascending order.
Internally it executes following query to show events listing:
SELECT a0_.name AS name_3, a0_.countryIso AS countryIso_37, COALESCE(a1_.rank, 99999) AS sclr_34
FROM Event a0_
LEFT JOIN Ranking a1_ ON a0_.eventID = a1_.eventID AND (a1_.locale = 'en' OR a1_.locale IS NULL)
INNER JOIN Country a2_ ON a0_.countryIso = a2_.countryIso
GROUP BY a0_.eventID
ORDER BY sclr_34 ASC, a0_.name ASC
So the result is something like as follows :
event1 BEL 1
event2 AUS 2
event3 AUS 3
event4 AUS 4
event5 NLD 5
event6 NLD 6
event7 ESP 7
But now, I want to display events per event per country. So the output will be like :
event1 BEL 1
event2 AUS 2
event5 NLD 5
event7 ESP 7
So, I believe query will be now like below :
SELECT * FROM (
SELECT a0_.name AS name_3, a0_.countryIso AS countryIso_37, COALESCE(a1_.rank, 99999) AS sclr_34
FROM Event a0_
LEFT JOIN Ranking a1_ ON a0_.eventID = a1_.eventID AND (a1_.locale = 'en' OR a1_.locale IS NULL)
INNER JOIN Country a2_ ON a0_.countryIso = a2_.countryIso
GROUP BY a0_.eventID
ORDER BY sclr_34 ASC, a0_.name ASC
) as t
GROUP BY t.countryIso_37 ORDER BY t.sclr_34 ASC limit 6;
But, with ZF2 and doctrine I am unable to prepare above query.
How can I achieve this using ZF2 and doctrine?

CollectionType with multiple entity type + Ajax

I have a problem creating a form by including a formType that are 2 related entities and that must be combined with a CollectionType.
I use the library jquery.collection for CollectionType ( http://symfony-collection.fuz.org/symfony3/ )
My entities
Product
namespace BBW\ProductBundle\Entity\Product;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Product
*
* #ORM\Table(name="product_product")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Product\ProductRepository")
*/
class Product
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute_group")
*/
private $attributeGroups;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", inversedBy="products", fetch="EAGER")
* #ORM\JoinTable(name="product_join_attribute")
*/
private $attributes;
/**
* Constructor
*/
public function __construct()
{
$this->attributeGroups = new \Doctrine\Common\Collections\ArrayCollection();
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Product
*/
public function addAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups[] = $attributeGroup;
return $this;
}
/**
* Remove attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*/
public function removeAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup)
{
$this->attributeGroups->removeElement($attributeGroup);
}
/**
* Get attributeGroups
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributeGroups()
{
return $this->attributeGroups;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return Product
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
}
AttributeGroup
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* AttributeGroup
*
* #ORM\Table(name="product_attribute_group")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeGroupRepository")
*/
class AttributeGroup
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\OneToMany(targetEntity="BBW\ProductBundle\Entity\Attribute\Attribute", mappedBy="attributeGroup", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_id")
*/
private $attributes ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributeGroups", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Constructor
*/
public function __construct()
{
$this->attributes = new \Doctrine\Common\Collections\ArrayCollection();
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*
* #return AttributeGroup
*/
public function addAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes[] = $attribute;
return $this;
}
/**
* Remove attribute
*
* #param \BBW\ProductBundle\Entity\Attribute\Attribute $attribute
*/
public function removeAttribute(\BBW\ProductBundle\Entity\Attribute\Attribute $attribute)
{
$this->attributes->removeElement($attribute);
}
/**
* Get attributes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAttributes()
{
return $this->attributes;
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return AttributeGroup
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Attributes
namespace BBW\ProductBundle\Entity\Attribute;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Attribute
*
* #ORM\Table(name="product_attribute")
* #ORM\Entity(repositoryClass="BBW\ProductBundle\Repository\Attribute\AttributeRepository")
*/
class Attribute
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="BBW\ProductBundle\Entity\Attribute\AttributeGroup", inversedBy="attributes", cascade={"persist"}, fetch="EAGER")
* #ORM\JoinColumn(name="attribute_group_id")
*/
private $attributeGroup ;
/**
* #var array
* #ORM\ManyToMany(targetEntity="BBW\ProductBundle\Entity\Product\Product", mappedBy="attributes", cascade={"persist"}, fetch="EAGER")
*/
private $products;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set attributeGroup
*
* #param \BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup
*
* #return Attribute
*/
public function setAttributeGroup(\BBW\ProductBundle\Entity\Attribute\AttributeGroup $attributeGroup = null)
{
$this->attributeGroup = $attributeGroup;
return $this;
}
/**
* Get attributeGroup
*
* #return \BBW\ProductBundle\Entity\Attribute\AttributeGroup
*/
public function getAttributeGroup()
{
return $this->attributeGroup;
}
/**
* Constructor
*/
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*
* #return Attribute
*/
public function addProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products[] = $product;
return $this;
}
/**
* Remove product
*
* #param \BBW\ProductBundle\Entity\Product\Product $product
*/
public function removeProduct(\BBW\ProductBundle\Entity\Product\Product $product)
{
$this->products->removeElement($product);
}
/**
* Get products
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getProducts()
{
return $this->products;
}
}
Explanation
My product must belong to an attribute group and must contain attributes. I have a relationship between my AttributeGroup entity and a Relationship with Attribute.
My Entity AttributeGroup has a relationship with Attribute (functional).
Form Types
In my form I have a CollectionType with another formType as entry_type I have created with two entity types (AttributeGroups / Attributes)
FormType for product
namespace BBW\ProductBundle\Form\Product;
use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
use BBW\CoreBundle\FormTypes\SwitchType;
use BBW\MediaBundle\Transformers\MediaTransformer;
use BBW\ProductBundle\Form\Attribute\Type\AttributeType;
use Doctrine\ORM\Mapping\Entity;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductEditType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$locale = $options['int_service'];
$builder
->add('attributes', CollectionType::class, array(
'entry_type' => AttributeType::class,
'entry_options' => array(
'label' => false
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'attr' => array(
'class' => 'attributes-selector'
),
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'bbw_productbundle_edit_product';
}
}
AttributeType
/**
* Form Type: Fields add in product edit form
*/
namespace BBW\ProductBundle\Form\Attribute\Type;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AttributeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('attributeGroups', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\AttributeGroup',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributeGroup'
),
))
->add('attributes', EntityType::class, array(
'label' => false,
'class' => 'BBW\ProductBundle\Entity\Attribute\Attribute',
'choice_label' => 'translate.name',
'attr' => array(
'class' => 'choiceAttributes'
)
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BBW\ProductBundle\Entity\Product\Product',
'int_service' => ['fr'],
'translation_domain' => 'ProductBundle'
));
}
}
Explanation
The two entities (AttributeGroups / Attributes) are two linked drop-down lists. When I select a group, I must display the data of this group in the second drop-down list. This part works well. I can duplicate as much as I want (with the CollectionType).
First problem when i submit the form:
"Could not determine access type for property "attributeGroups" ".
Second problem when i set "mapped => false" in my two entities:
"Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "BBW\ProductBundle\Entity\Product\Product#$attributes", got "BBW\ProductBundle\Entity\Product\Product" instead."
Screeshoots dump/form
Conclusion
I think my problem is a mapping problem, having tested a lot of code and explanation on the symfony doc or other site, I could not solve my problem. If anyone could help me out and explain the good work of a collectionType with 2 entity and if that is possible. If you have examples of codes I am taker too.
Thank you in advance for your help.

Magento 2 - Set collection limit in a Model

I created a model file which I would like a method inside me to return products with a limit.
Below my model:
<?php
namespace Test\Ice\Model;
class Data extends \Magento\Framework\App\Helper\AbstractHelper {
/**
* System configuration values
*
* #var array
*/
protected $_config;
/**
* Store manager interface
*
*/
protected $_storeManager;
/**
* Product interface
*
*/
protected $_product;
/**
* Initialize
*
* #param Magento\Framework\App\Helper\Context $context
* #param Magento\Catalog\Model\ProductFactory $productFactory
* #param Magento\Store\Model\StoreManagerInterface $storeManager
* #param Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* #param array $data
*/
public function __construct(
\Magento\Framework\App\Helper\Context $context,
\Magento\Catalog\Model\ResourceModel\Product\Collection $collection,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
array $data = []
) {
$this->_product = $collection;
$this->_storeManager = $storeManager;
parent::__construct($context, $data);
}
public function getProducts() {
$limit = 10;
return $this->_product->getSelect()->limit($limit);
}
}
The problem is that it does not make the limit, but always returns all products in the collection.
Where am I wrong?
Thank you
Use
$this->_product->getCollection()->setPageSize(10)->setCurPag‌​e(1);

Symfony2 FOSRestBundle timestamp validation fails

I'm trying to send a post request including two timestamps to my REST api.
The problem is that the timestamps are marked as invalid. "This value is not valid."
What am I doing wrong?
This is the request:
POST http://localhost:8000/app_test.php/api/projects/1/tasks/1/timetrackings
Accept: application/json
Content-Type: application/json
{"timeStart":1390757625,"timeEnd":1390757625,"projectMember":1}
The Controller looks as follows:
class MemberController extends BaseApiController implements ClassResourceInterface
{
public function postAction($projectId, $taskId, Request $request)
{
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$this->findProject($em, $projectId);
$task = $this->findTask($em, $projectId, $taskId);
$request->request->add(array(
'task' => $taskId,
));
$form = $this->createForm(new TimeTrackType(), new TimeTrack());
$form->submit($request->request->all());
if ($form->isValid())
{
/** #var TimeTrack $tracking */
$tracking = $form->getData();
$task->addTimeTrack($tracking);
$em->flush();
return $this->redirectView(
$this->generateUrl('api_get_project_task_timetracking', array(
'projectId' => $projectId,
'taskId' => $taskId,
'trackingId' => $tracking->getId(),
)),
Codes::HTTP_CREATED
);
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
}
The TimeTrackType class:
namespace PMTool\ApiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TimeTrackType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('timeStart', 'datetime', array(
'input' => 'timestamp',
))
->add('timeEnd', 'datetime', array(
'input' => 'timestamp',
))
->add('projectMember')
->add('task')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'PMTool\ApiBundle\Entity\TimeTrack',
'csrf_protection' => false,
));
}
/**
* #return string
*/
public function getName()
{
return 'timetrack';
}
}
The entity class:
namespace PMTool\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \DateTime;
/**
* TimeTrack
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="PMTool\ApiBundle\Entity\TimeTrackRepository")
*/
class TimeTrack
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DateTime
*
* #ORM\Column(name="timeStart", type="datetime")
*/
private $timeStart;
/**
* #var DateTime
*
* #ORM\Column(name="timeEnd", type="datetime")
*/
private $timeEnd;
/**
* #var ProjectMember
*
* #ORM\ManyToOne(targetEntity="ProjectMember")
*/
private $projectMember;
/**
* #var Task
*
* #ORM\ManyToOne(targetEntity="Task", inversedBy="timeTracks")
* #ORM\JoinColumn(name="taskId", referencedColumnName="id")
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set timeStart
*
* #param DateTime $timeStart
* #return TimeTrack
*/
public function setTimeStart($timeStart)
{
$this->timeStart = $timeStart;
return $this;
}
/**
* Get timeStart
*
* #return DateTime
*/
public function getTimeStart()
{
return $this->timeStart;
}
/**
* Set timeEnd
*
* #param DateTime $timeEnd
* #return TimeTrack
*/
public function setTimeEnd($timeEnd)
{
$this->timeEnd = $timeEnd;
return $this;
}
/**
* Get timeEnd
*
* #return DateTime
*/
public function getTimeEnd()
{
return $this->timeEnd;
}
/**
* #return \PMTool\ApiBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
/**
* #param \PMTool\ApiBundle\Entity\Task $task
* #return $this
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* #return \PMTool\ApiBundle\Entity\ProjectMember
*/
public function getProjectMember()
{
return $this->projectMember;
}
/**
* #param \PMTool\ApiBundle\Entity\ProjectMember $projectMember
* #return $this
*/
public function setProjectMember($projectMember)
{
$this->projectMember = $projectMember;
return $this;
}
}
You can use a transformer to achieve this. (See Symfony Transformers)
Here is an Example of my FormType:
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
...
$transformer = new DateTimeToTimestampTransformer();
$builder->add($builder->create('validFrom', 'text')
->addModelTransformer($transformer)
)
Be sure to use "'text'" as an input type, otherwise it didn't work for me.
the input option is just for the model side of your underlying data object. In your case it should be datetime.
Your problem is, that you want to transform the timestamp into view data that the symfony form datetime form type does recognise. And I dont know how to do that, unfortunately.
http://symfony.com/doc/current/reference/forms/types/datetime.html#input

Resources