Symfony 2 knplabs form translation - best way to change the referenceColumName - doctrine-orm

A2lix required 'id' as referenceColumName. But I've entities with a different columName (e.g. ProductID instead of id). So I get error when I try to update my DB via doctrine:schema:update --force.
Example of my entity
class Period
{
use ORMBehaviors\Translatable\Translatable,
ORMBehaviors\Timestampable\Timestampable,
ORMBehaviors\SoftDeletable\SoftDeletable
;
/**
* #var integer
*
* #ORM\Column(name="PeriodID", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
Part of the TranslatableSubscriber class of KnpDoctrineBehaviors Package
...
private function mapTranslatable(ClassMetadata $classMetadata)
{
if (!$classMetadata->hasAssociation('translations')) {
$classMetadata->mapOneToMany([
'fieldName' => 'translations',
'mappedBy' => 'translatable',
'indexBy' => 'locale',
'cascade' => ['persist', 'merge', 'remove'],
'fetch' => $this->translatableFetchMode,
'targetEntity' => $classMetadata->getReflectionClass()->getMethod('getTranslationEntityClass')->invoke(null),
'orphanRemoval' => true
]);
}
}
private function mapTranslation(ClassMetadata $classMetadata)
{
if (!$classMetadata->hasAssociation('translatable')) {
$classMetadata->mapManyToOne([
'fieldName' => 'translatable',
'inversedBy' => 'translations',
'fetch' => $this->translationFetchMode,
'joinColumns' => [[
'name' => 'translatable_id',
'referencedColumnName' => 'id',
'onDelete' => 'CASCADE'
]],
'targetEntity' => $classMetadata->getReflectionClass()->getMethod('getTranslatableEntityClass')->invoke(null),
]);
}
$name = $classMetadata->getTableName().'_unique_translation';
if (!$this->hasUniqueTranslationConstraint($classMetadata, $name)) {
$classMetadata->table['uniqueConstraints'][$name] = [
'columns' => ['translatable_id', 'locale' ]
];
}
if (!$classMetadata->hasField('locale')) {
$classMetadata->mapField(array(
'fieldName' => 'locale',
'type' => 'string'
));
}
}
...
Any idea or help would be appreciate.
Many thanks ;-)

You can use:
association override
I tried to override properties but they are created by the subscriber and I got exceptions. You can also re-declare the annotation manyToOne on your entity class.
It works for me.

Related

Codeception/Doctrine2: Calling 'grabEntityFromRepository' before 'canSeeInRepository' causes the latter to break

An odd one. I'm testing a REST API using Codeception and Symfony4/Doctrine2.
If a test has a single call to $I->canSeeInRepository(...);, it works fine.
However, if I make a call to $I->grabEntityFromRepository(...); (which works fine) before making this call, the $I->canSeeInRepository(...); call fails.
It seems like some kind of Doctrine issue.
Can anybody shed some light? Many thanks.
/**
* #param ApiTester $I
*/
public function testConvertOfferToTemplate(ApiTester $I)
{
$I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded');
/** #var \App\ServiceProviderBundle\Entity\Offer $offer */
$offer = $I->grabEntityFromRepository(\App\ServiceProviderBundle\Entity\Offer::class, [
'notes' => 'SOME NOTES - Custom Offer final draft',
'dateArchived' => null,
]);
$I->sendPOST('/offer-templates', json_encode([
'name' => 'Codeception Created Template From Offer',
'offer_id' => $offer->getId(),
]));
$json = array(
'offer_template' =>
array(
'name' => 'Codeception Created Template From Offer',
'charge_period' => $offer->getChargePeriod(),
'charge_amount' => $offer->getChargeAmount(),
'charge_currency' => $offer->getChargeCurrency(),
'terms' => $offer->getTerms(),
'_embedded' =>
array(),
),
);
$I->seeResponseContainsJson($json);
$I->canSeeInRepository(\App\ServiceProviderBundle\Entity\OfferTemplate::class, [
'name' => 'Codeception Created Template From Offer', // FAILS
]);
}
/**
* #param ApiTester $I
*/
public function testOfferCreatedFromTemplate(ApiTester $I)
{
$I->canSeeInRepository(\App\ServiceProviderBundle\Entity\OfferTemplate::class, [
'name' => 'Codeception Created Template From Offer', // PASSES
]);
}

Inserting data to database using zf3 and doctrine

i'm trying to insert data to database but submitted forms does nothing.
this is my service manager:
class AutosManager
{
/**
* Entity manager.
* #var Doctrine\ORM\EntityManager;
*/
private $entityManager;
/**
* Constructor.
*/
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function addNewAutos($data)
{
$autos = new Autos();
$autos->setTitle($data['title']);
$autos->setDescription($data['description']);
$currentDate = date('Y-m-d H:i:s');
$autos->setDateCreated($currentDate);
$this->entityManager->persist($autos);
$this->entityManager->flush();
}
this is my controller addAction
public function addAction()
{
// Create the form.
$form = new PostForm();
if ($this->getRequest()->isPost()) {
// Get POST data.
$data = $this->params()->fromPost();
// Fill form with data.
$form->setData($data);
if ($form->isValid()) {
// Get validated form data.
$data = $form->getData();
$this->AutosManager->addNewAutos($data);
return $this->redirect()->toRoute('retrieve');
}
}
return new ViewModel([
'form' => $form
]);
}
i can retrieve data from database to the index page but i cannot add. hope to find the solution.
this is my Autos Entity
namespace Retrieve\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="\Retrieve\Repository\AutosRepository")
* #ORM\Table(name="auto")
*/
class Autos
{
/**
* #ORM\Id
* #ORM\Column(name="id")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\Column(name="title")
*/
protected $title;
/**
* #ORM\Column(name="description")
*/
protected $description;
/**
* #ORM\Column(name="featured")
*/
protected $featured;
/**
* #ORM\Column(name="date_created")
*/
protected $dateCreated;
/**
* Returns ID of this post.
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Sets ID of this post.
* #param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* Returns title.
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Sets title.
* #param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Returns featured.
* #return integer
*/
public function getFeatured()
{
return $this->featured;
}
/**
* Sets featured.
* #param integer $featured
*/
public function setFeatured($featured)
{
$this->featured = $featured;
}
/**
* Returns post description.
*/
public function getDescription()
{
return $this->description;
}
/**
* Sets post description.
* #param type $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* Returns the date when this post was created.
* #return string
*/
public function getDateCreated()
{
return $this->dateCreated;
}
/**
* Sets the date when this post was created.
* #param string $dateCreated
*/
public function setDateCreated($dateCreated)
{
$this->dateCreated = $dateCreated;
}
}
hope this helps to find solution.
I found the problem: it was an inputfilter element I wasn't using that was authenticating in forms. But the solution only brings me to a different problem:
Notice: Undefined index: title in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 38
Notice: Undefined index: description in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 39
Notice: Undefined index: featured in C:\xampp\htdocs\ameyaw\module\BusinessGhana\src\Service\AutosManager.php on line 58
Message:
An exception occurred while executing 'INSERT INTO auto (title, description, featured, date_created) VALUES (?, ?, ?, ?)' with params [null, null, null, "2017-06-15 05:04:44"]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null
this is my form and fieldset
use Zend\Form\Fieldset;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use BusinessGhana\Entity\Autos;
class AddFieldset extends Fieldset
{
protected $objectManager;
public function init()
{
$this->add([
'type' => 'text',
'name' => 'title',
'attributes' => [
'id' => 'autoTitle'
],
'options' => [
'label' => 'Title',
'display_empty_item' => true,
'empty_item_label' => 'Maximum of 60 characters',
],
]);
$this->add([
'type' => 'textarea',
'name' => 'description',
'attributes' => [
'id' => 'autoDescription'
],
'options' => [
'label' => 'Description',
'display_empty_item' => true,
'empty_item_label' => 'description',
],
]);
$this->add([
'type' => 'radio',
'name' => 'featured',
'attributes' => [
'id' => 'autoFeatured'
],
'options' => array(
'label' => 'Featured',
'value_options' => array(
array('value' => '0',
'label' => 'No',
'selected' => true,
'label_attributes' => array(
'class' => 'col-sm-2 btn btn-default',
),
),
array(
'value' => '1',
'label' => 'Yes',
'label_attributes' => array(
'class' => 'col-sm-2 btn btn-danger',
),
),
),
'column-size' => 'sm-12',
'label_attributes' => array(
'class' => 'col-sm-2',
),
),
]);
}
}
use Zend\Form\Form;
//use Zend\InputFilter\InputFilter;
class AddForm extends Form
{
public function init()
{
$this->add([
'name' => 'dependentForm',
'type' => AddFieldset::class,
]);
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Submit',
],
]);
}
}
i know hydration can solve this problem but i dont know how to use it yet.

ZF2 form removing nested collection after validation

I have three related Doctrine entities that I am trying to use with Zend Form collections.
The entities are InvoiceItems -> (one to many) -> InvoiceItemOptions -> (one to many) InvoiceItemOptionValues.
When I add a new item all options and values are populating the entity correctly. However when I edit a item the values are being removed from the options entity. The form displays correctly so the values are being pulled from the database and DoctrineObject hydrator is populating the form. The values are lost after the form is validated.
My entity code
InvoiceItems
/**
* #var \Doctrine\ORM\PersistentCollection
*
* #ORM\OneToMany(targetEntity="Application\Entity\InvoiceItemOptions", cascade={"persist"}, orphanRemoval=true, mappedBy="invoiceItem")
* })
*/
private $options;
InvoiceItemOptions
/**
* #var \Application\Entity\InvoiceItems
*
* #ORM\ManyToOne(targetEntity="Application\Entity\InvoiceItems", cascade={"persist"}, inversedBy="options")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="invoice_item_id", referencedColumnName="invoice_item_id")
* })
*/
private $invoiceItem;
/**
* #var \Doctrine\ORM\PersistentCollection $values
*
* #ORM\OneToMany(targetEntity="Application\Entity\InvoiceItemOptionValues", cascade={"persist"}, orphanRemoval=true, mappedBy="invoiceItemOption")
* })
*/
private $values;
InvoiceItemOptionValues
/**
* #var integer
*
* #ORM\Column(name="invoice_item_option_value_id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $invoiceItemOptionValueId;
/**
* #var \Application\Entity\InvoiceItemOptions
*
* #ORM\ManyToOne(targetEntity="Application\Entity\InvoiceItemOptions", cascade={"persist"}, inversedBy="values")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="invoice_item_option_id", referencedColumnName="invoice_item_option_id")
* })
*/
private $invoiceItemOption;
/**
* #var string
*
* #ORM\Column(name="value", type="text", length=65536, nullable=false)
*/
private $value;
/**
* #var string
*
* #ORM\Column(name="price", type="decimal", precision=9, scale=2, nullable=false)
*/
private $price;
And in my fieldsets I have
InvoiceItemFieldset
$this->add(array(
'type' => 'Collection',
'name' => 'options',
'options' => array(
'count' => 0,
'should_create_template' => TRUE,
'template_placeholder' => '__OPTION__',
'allow_add' => TRUE,
'allow_remove' => TRUE,
'target_element' => $this->invoiceItemOptionFieldset,
),
));
InvoiceItemOptionFieldset
$this->add(array(
'type' => 'Collection',
'name' => 'values',
'options' => array(
'count' => 0,
'should_create_template' => TRUE,
'template_placeholder' => '__VALUE__',
'target_element' => $this->invoiceItemOptionValuesFieldset,
),
));
InvoiceItemOptionValuesFieldset
public function init()
{
$this->add(array(
'name' => 'invoiceItemOptionValueId',
'type' => 'Hidden'
));
$this->add(array(
'name' => 'value',
'type' => 'Textarea',
'options' => array(
'label' => _('Value:'),
'label_attributes' => array('class' => 'required'),
),
'attributes' => array(
'cols' => 30,
'rows' => 5,
),
));
$this->add(array(
'name' => 'price',
'type' => 'Text',
'options' => array(
'label' => _('Price Inc. VAT:'),
),
'attributes' => array(
'size' => 10,
'maxlength' => 10,
),
));
}
EditItemModel
use Zend\Stdlib\Parameters;
...
public function processForm(Parameters $postData)
{
$entityManager = $this->getEntityManager();
$this->form->setData($postData);
if ($this->form->isValid()) {
// values lost here
$this->form->bind($this->entity);
$this->setFormValid(TRUE);
if ($this->entity->getQuantity() < 1) {
$this->entity->setQuantity(1);
}
$goodsTotal = $this->entity->getPriceEachIncVat() * $this->entity->getQuantity();
$optionsTotal = 0.00;
foreach ($this->entity->getOptions() as $option) {
foreach ($option->getValues() as $value) { // entity has no values
if ($value->getPrice() > 0) {
$optionsTotal = $optionsTotal + ($value->getPrice() * $this->entity->getQuantity());
}
}
}
$this->invoiceModel->calculateItemsVat($this->entity, $goodsTotal, $optionsTotal);
$entityManager->persist($this->entity);
if ($this->flushEntityManager($entityManager)) {
return $this->invoiceModel->calculateInvoiceTotals($this->getInvoice()->getInvoiceId());
}
}
return FALSE;
}
Finally my post data
Array
(
[items] => Array
(
[options] => Array
(
[0] => Array
(
[name] => Test Option 0
[invoiceItemOptionId] => 37
[values] => Array
(
[0] => Array
(
[value] => Test Option 0 Value 0
[price] => 0.00
[invoiceItemOptionValueId] => 37
)
[1] => Array
(
[value] => Test Option 0 Value 1
[price] => 29.99
[invoiceItemOptionValueId] => 38
)
)
)
)
[title] => Title
[sku] =>
[quantity] => 2
[priceEachIncVat] => 1000.00
[vatRate] => 1
[invoiceItemId] => 20
)
)
I am using Zend Framework version 2.5.1 and Doctrine ORM Module version 1.0.0
Hopefully someone knows what is going on here, many thanks in advance.
I have found a solution.
Adding the line $this->form->setBindOnValidate(FormInterface::BIND_MANUAL); before form validation solved the problem. I also added $this->form->bind($this->entity) after validation;
Hope this helps someone.
EDIT
I also found that the array collection for values in InvoiceItemOptions entity was not being cloned during hydration so the InvoiceItemOptionValues were wrong. I solved this issue by adding the magic method __clone() to my InvoiceItemOptions entity.
public function __clone()
{
$this->values = new ArrayCollection();
}

Doctrine Object Select not hydrating as object

I have a form with a fieldset which contains a Doctrine ObjectSelect element.
$this->add(array(
'name' => 'vatRate',
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'options' => array(
'label' => _('VAT Rate:'),
'object_manager' => $this->getEntityManager(),
'target_class' => 'Application\Entity\VatRates',
'property' => 'title',
'is_method' => true,
'find_method' => array(
'name' => 'getVatRatesOrderRate',
),
'label_generator' => function($targetEntity) {
return $targetEntity->getTitle() . ' (' . $targetEntity->getVatRate() . '%)';
},
'disable_inarray_validator' => TRUE,
),
));
In my invoiceItems entity I have
/**
* #var \Application\Entity\VatRates
*
* #ORM\ManyToOne(targetEntity="Application\Entity\VatRates")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="vat_rate_id", referencedColumnName="vat_rate_id")
* })
*/
private $vatRate;
/**
*
* #param VatRates|NULL $vatRate
* #return \Application\Entity\InvoiceItems
*/
public function setVatRate(VatRates $vatRate = NULL)
{
$this->vatRate = $vatRate;
return $this;
}
The problem I am having is that when I validate the form Doctrine's DoctrineObject hydrator is not converting the vatRate to an entity before passing it to the setter. I get the following error.
Catchable fatal error: Argument 1 passed to Application\Entity\InvoiceItems::setVatRate() must be an instance of Application\Entity\VatRates, integer given, called in C:\Users\User\OneDrive\Documents\My Webs\freedomsnew\vendor\doctrine\doctrine-module\src\DoctrineModule\Stdlib\Hydrator\DoctrineObject.php on line 282 and defined in C:\Users\User\OneDrive\Documents\My Webs\freedomsnew\module\Application\src\Application\Entity\InvoiceItems.php on line 339
As can be seen by the error message the integer vatRateId is being sent to the invoiceItems entity setVatRate method.
I have other forms/fieldsets setup in a similar way and all work with no problems. Does anyone know what I have done wrong here?
The problem was that Doctrine had cached the entity. Clearing the cache solved the problem.
php ./vendor/doctrine/doctrine-module/bin/doctrine-module orm:clear-cache:query
php ./vendor/doctrine/doctrine-module/bin/doctrine-module orm:clear-cache:metadata
php ./vendor/doctrine/doctrine-module/bin/doctrine-module orm:clear-cache:result
I setup a batch file to do this from now on.

Dynamic added fieldset/ collection not added to database Zend 2 Doctrin 2

After browsing several tutorials reagrding Zend2 /Doctrine 2 and Fieldsets i finally figured out the filedset/collction.
But new "fields" wont be added to the database table. Any changes in the existing elements are stored. Major Class Organization callsthe fieldset ActBusinessCountry:
Major Class Organization
class Organization {
protected $inputFilter;
/**
* #ORM\Id
* #ORM\Column(type="integer");
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $organizational;
/**
* #ORM\Column(type="string")
*/
protected $structure;
/**
* #param \Doctrine\Common\Collections\ArrayCollection
* #ORM\OneToMany(targetEntity="People\Entity\ActBusinessCountry",mappedBy="company",cascade={"persist", "merge", "refresh", "remove"})
*/
protected $organizaton_opcountry;
public function __construct()
{
$this->organizaton_opcountry = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #param \Doctrine\Common\Collections\ArrayCollection $organizaton_opcountry
*/
public function addOrganizaton_opcountry(Collection $organizaton_opcountry)
{
foreach ($organizaton_opcountry as $opcountry) {
$this->organizaton_opcountry->add($opcountry);
}
return $this->organizaton_opcountry;
}
public function removeOrganizaton_opcountry(Collection $organizaton_opcountry)
{
foreach ($organizaton_opcountry as $opcountry) {
$tag->setCompany(null);
$this->organizaton_opcountry->removeElement($opcountry);
}
}
/**
* #return Collection
*/
public function getOrganizaton_opcountry()
{
return $this->organizaton_opcountry;
}
Sub class/fieldset is ActBusinessCountry
class ActBusinessCountry {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\ManyToOne(targetEntity="People\Entity\Organization",inversedBy="organizaton_opcountry")
* #ORM\JoinColumn(name="company_id", referencedColumnName="id")
*/
public $company;
/**
* #ORM\Column(type="string")
*/
public $country;
/**
* #ORM\Column(type="string")
*/
public $company_id;
/**
* Allow null to remove association
*/
public function setId($id = null)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function getCompany()
{
return $this->company;
}
public function setCompany(Company $company = null)
{
$this->company = $company;
}
public function getCountry()
{
return $this->country;
}
public function setCountry($country)
{
$this->country = $country;
}
}
Organization Form:
$countrySelect = new ActBusinessCountryFieldset($objectManager);
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'organizaton_opcountry',
'options' => array(
'should_create_template' => true,
'use_as_base_fieldet' => true,
'count' => 1,
'allow_add' => true,
'target_element' => $countrySelect,
),
));
Fielset Elements:
class ActBusinessCountryFieldset extends Fieldset implements ObjectManagerAwareInterface
{
protected $objectManager;
public function __construct(ObjectManager $objectManager)
{
$this->setObjectManager($objectManager);
parent::__construct('fieldset');
$this ->setHydrator(new DoctrineHydrator($objectManager, 'People\Entity\ActBusinessCountry'))
->setObject(new \People\Entity\ActBusinessCountry());
$this->add(array(
'type' => 'Zend\Form\Element\Hidden',
'name' => 'id'
));
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'country',
'attributes' => array(
'class' => 'form-control input-small',
),
'options' => array(
'object_manager' => $this->getObjectManager(),
'target_class' => 'People\Entity\Country',
'value' => 'country',
'property' => 'country_name',
'class'=>'form-control',
'label_attributes' => array(
'class'=> 'col-sm-3 control-label',
),
),
));
}
public function getInputFilterSpecification()
{
return array(
'id' => array(
'required' => false
)
);
return array(
'country' => array(
'required' => true
)
);
}
Controller Flushing Part:
if ($this->request->isPost()) {
// Cancel button
if(isset($_POST['cancel'])) {
echo "<script>window.close();</script>";
}
$form->setData($this->request->getPost());
var_dump($this->request->getPost('organizaton_opcountry'));
var_dump($queryresult->getOrganizaton_opcountry());
//$queryresult->addOrganizaton_opcountry();
if ($form->isValid()) {
// Security request
if ($this->isAllowed('admin_res','admin_priv')) {
$form->bindValues();
$this->getEntityManager()->persist($queryresult);
$this->getEntityManager()->flush();
}
//echo "<script>window.close();</script>";
}
}
I am afraid, that i just missed a point. Though the post var_dump($this->request->getPost('organizaton_opcountry')); in the controller does output the following, after adding a third element and submitting:
array
0 =>
array
'id' => string '1' (length=1)
'country' => string 'Chad' (length=4)
1 =>
array
'id' => string '2' (length=1)
'country' => string 'Bermuda' (length=7)
2 =>
array
'id' => string '' (length=0)
'country' => string '(Not Specified)' (length=15)
Maybe you guys have an idea, or you have had the same problem before.
Thank you very much for any hint.
Kind regards,
David
Maybe u take a look at:
Saving a Doctine 2 Entity that contains an ObjectSelect element using Zend Form
Check the inputFilter in the Fieldset and add an inputfilter for the fieldset in the form e.g.:
$this->setValidationGroup(array(
'User' => array(
'name',
'role' // <- Fieldset
)
));