I have Symfony 2.6 application and using doctrine-odm-bundle 3.0.2 (doctrine-odm: 1.1.2, mongodb: 1.4.0).
My document has referenceMany and referenceOne in attributes and when I create new instance of it, fill fields and persist - it goes fine in the begining. I can create few nearly empty documents, with referenced document(s) or without and it works fine. At some point I am trying to add new item in the database and getting an error:
E11000 duplicate key error collection: test.Product index: _id_ dup key: { : 0 }
The message is clear - I can see that there was a document added to the collection with id = 0, therefore second one can't go -> duplicate entry. But why it suddenly starts to return "0" for id? Even though, I checked doctrine_increment_ids collection - counter for id is being incremented. But $product->getId() becomes "0" after persist.
If I drop the database and start all over - it works, I can still add new products in the collection. Let's say I successfully created 12 products. Creating 13th resulting a document with id=0 being persisted in the collection. 14th fails with duplicate error.
Can you please help to troubleshoot or suggest an idea where does it go wrong?
P.S> I am not considering an upgrade of Symfony2 (at this point) neither as doctrine-odm-bundle (it depends on newer Symfony2 as well) - migration efforts are quite high and I am not sure it will fix the issue. First I want to find out the root cause.
// Document Product
/**
* #MongoDB\Document
* #MongoDB\HasLifecycleCallbacks
*/
class Product
{
/** #MongoDB\Id(strategy="ALNUM", type="int") */
protected $id;
/**
* #Gedmo\ReferenceOne(type="entity", class="Entity\User", inversedBy="products", identifier="userId")
*/
protected $user;
/**
* #MongoDB\Field(name="user_id", type="int")
*/
protected $userId;
/**
* #MongoDB\ReferenceMany(
* targetDocument="Picture",
* discriminatorMap={"file" = "File", "picture" = "Picture"},
* discriminatorField="discr",
* defaultDiscriminatorValue="picture"
* )
* #Assert\Valid
*/
protected $pictures;
...
}
// Entity User
/**
* User entity
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var ArrayCollection $textures
*
* #Gedmo\ReferenceMany(type="document", class="Document\Product", mappedBy="user")
*/
protected $products;
...
}
// Document Picture
/**
* #MongoDB\Document
* #MongoDB\InheritanceType("SINGLE_COLLECTION")
* #MongoDB\DiscriminatorField("discr")
* #MongoDB\DiscriminatorMap({"file" = "File", "picture" = "Picture"})
* #MongoDB\DefaultDiscriminatorValue("picture")
* #MongoDB\HasLifecycleCallbacks
*/
class Picture
{
/**
*
* #MongoDB\Id(strategy="ALNUM", type="int")
*/
protected $id;
/**
* #MongoDB\ReferenceOne(targetDocument="Product")
*
* #var Product $product
*/
protected $product;
...
}
Documentation reading always helps (generation strategies). Basically, strategy="ALNUM" and type="int" just can't go together :)
Change strategy to INCREMENT and remove type="int" if you want to have integers in your _id.
Or you can change type to string to continue with _id being an alphanumeric string.
Related
I'm trying to create MongoDB database with some references within Symfony.
In my context I have 2 documents Customer and Meeting, One Customer can have Many Meeting so that what I did :
Meeting.php
<?php
namespace FrontOfficeBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Meeting
{
/**
* #MongoDB\Id
* #MongoDB\ReferenceOne(targetDocument="Customer")
*/
protected $id;
/**
* #MongoDB\Field(type="timestamp")
*/
protected $creationDate;
...
Customer.php
<?php
namespace FrontOfficeBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class Customer
{
/**
* #MongoDB\Id()
* #MongoDB\ReferenceMany(targetDocument="Meeting")
*/
protected $id;
/**
* #MongoDB\Field(type="string")
*/
protected $username;
...
and then when I run the command line:
php bin/console doctrine:mongodb:schema:update
I got :
No identifier/primary key specified for Document 'FrontOfficeBundle\Document\Meeting'. Every Document must have an identifier/primary key.
I tried by using #MongoDB\UniqueIndex() but no way.
I think that #MongoDB\Id is supposed as an identifier !!!
Versions
Symfony 3.2
MongoDB 3.4.4
Any ideas ?
Thanks you.
Finally I found a solution, first I added a field called $meetings in the document Customer and an other $customer in the document meeting like this :
Customer.php
/**
* #MongoDB\ReferenceMany(targetDocument="meeting", mappedBy="customer")
*/
protected $meetings;
Meeting.php
/**
* #MongoDB\ReferenceOne(targetDocument="customer", inversedBy="meetings")
*/
protected $customer;
Then run the command line to generate setters and getters:
php bin/console doctrine:mongodb:generate:documents mybundleBundle
And when I run a fixtures (Creating one customer then its meeting), everything work fine, you'll notice that the relationship 1 to many has been defined in your document (I'm using compass to display mongoDB) as a One-to-Squillions model (for more details about 1-to-N relationship : (https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1) that's mean the son document (meeting) contains the reference of the parent as shown below :
Customer
Meeting
In MongoDB the #MongDB/Id is a primary key and the foreign key and never try to define references to this unique field.
Thanks for your attention.
I'm using Symfony 3.2 with doctrine and postgresql.
I've created an entity with a uuid as primary key.
My entity definition:
/**
* Booking
*
* #ORM\Table(name="booking")
* #ORM\Entity(repositoryClass="AppBundle\Repository\BookingRepository")
* #ORM\EntityListeners({"AppBundle\EventListener\BookingListener"})
*/
class Booking {
/**
* #var string
*
* #ORM\Column(type="guid")
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
}
In my controller I have a show action like this:
/**
* #Route("booking/{id}", name="booking_show")
* #Method({"GET"})
*/
public function showAction(Request $request, Booking $booking) {
...
}
Everything seems to work fine, but when I try to load a route putting an wrong value as an ID (i.e. /booking/hello123), I receive a:
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for uuid: "hello123"
Instead I would expect a 404.
Is there a way to capture this exception and redirect to a 404 page?
You can make use of Route Requirements - you can specify what conditions your parameter need to match to "qualify" to a certain route. This requirement is a regex, so all you need to do is to write a regex for an UUID
/**
* #Route("booking/{id}", name="booking_show", requirements={"id": "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"})
* #Method({"GET"})
*/
public function showAction(Request $request, Booking $booking) {
...
}
NOTE: Regex used above is just first result I found in Google for UUID regex, I didn't verify if it works
In the end if your id does not match regex, it does not match route and you should get 404.
you need to change the showaction
/**
* #Route("booking/{id}", name="booking_show")
* #Method({"GET"})
*/
public function showAction(Request $request,$bookingID) {
$em = $this->getDoctrine()->getManager();
$booking = $em->getRepository('AppBundle:Entity')->find(bookingID);
if(!$booking)
$this->createNotFoundException('No entity found with id :'.$bookingID);
...
}
By default Magento's Order REST API doesn't submit the store credit amount used for the order(customer_balance_amount col in the DB). I need to expose that to the API interface, but as of now am unable to. I tried two approaches:
http://magehit.com/blog/how-to-get-value-of-custom-attribute-on-magento-2-rest-api/ - using an observer, but that doesn't seem to have any reflection on the API data
and
http://www.ipragmatech.com/extend-magento2-rest-api-easy-steps/ - which I successfully tried, but it concerns actually creating a new ednpoint instead of overriding/extending the current API.
I was actually able to reproduce that by directly altering the OrderInterface and Order model inside the module-sales core module, but I want to achieve that the "proper" way instead of modifying core.
I would be thankful if anyone shares some knowledge how to do that.
Edit: adding the code that made the solution working, but the goal is to make it the proper way, not edit the core files like so:
vendor/magento/module-sales/Api/Data/OrderInterface.php:
/*
* Customer Balance Amount
*/
const CUSTOMER_BALANCE_AMOUNT = 'customer_balance_amount';
/**
* Returns customer_balance_amount
*
* #return float Customer Balance Amount
*/
public function getCustomerBalanceAmount();
/**
* Sets the customer_balance_amount for the order.
*
* #param float $amount
* #return $this
*/
public function setCustomerBalanceAmount($amount);
vendor/magento/module-sales/model/Order.php:
/**
* Returns customer_balance_amount
*
* #return float
*/
public function getCustomerBalanceAmount()
{
return $this->getData(OrderInterface::CUSTOMER_BALANCE_AMOUNT);
}
/**
* Sets the customer_balance_amount for the order.
*
* #param float $amount
* #return $this
*/
public function setCustomerBalanceAmount($amount)
{
return $this->setData(OrderInterface::CUSTOMER_BALANCE_AMOUNT, $amount);
}
Regards,
Alex
It looks like this is actually a bug, since Magento does define the balance columns as extension attributes in vendor/magento/module-customer-balance/etc/extension_attributes.xml
Looking at the GiftMessage module, the way to do this is via a plugin.
vendor/magento/module-gift-message/etc/di.xml
<type name="Magento\Sales\Api\OrderRepositoryInterface">
<plugin name="save_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderSave"/>
<plugin name="get_gift_message" type="Magento\GiftMessage\Model\Plugin\OrderGet"/>
</type>
\Magento\GiftMessage\Model\Plugin\OrderGet
/**
* Get gift message for order
*
* #param \Magento\Sales\Api\Data\OrderInterface $order
* #return \Magento\Sales\Api\Data\OrderInterface
*/
protected function getOrderGiftMessage(\Magento\Sales\Api\Data\OrderInterface $order)
{
$extensionAttributes = $order->getExtensionAttributes();
if ($extensionAttributes && $extensionAttributes->getGiftMessage()) {
return $order;
}
try {
/** #var \Magento\GiftMessage\Api\Data\MessageInterface $giftMessage */
$giftMessage = $this->giftMessageOrderRepository->get($order->getEntityId());
} catch (NoSuchEntityException $e) {
return $order;
}
/** #var \Magento\Sales\Api\Data\OrderExtension $orderExtension */
$orderExtension = $extensionAttributes ? $extensionAttributes : $this->orderExtensionFactory->create();
$orderExtension->setGiftMessage($giftMessage);
$order->setExtensionAttributes($orderExtension);
return $order;
}
I'm not sure whether this is my fault. But I find that select fields in mongodb doctrine queries can lead to horrible consequence.
For example, I have a Page document like:
class Page
{
/**
* #MongoDB\String
*/
private $title;
/**
* #MongoDB\Int
*/
private $nbClicks = 0;
/**
* #MongoDB\Int
*/
private $nbShares = 0;
// other fields ...
}
I may write some query like
$queryBuilder->select('title')->getQuery()->execute();
And somewhere behind this I do
$dm->flush();
nbClicks and nbShares of Pages I retrieved earlier are set to 0.
Is this something expected or might be a bug?
Thank you!
I am constructing a form for users to submit a DMCA complaint, and one of the design requirements is to allow them to enter one or more URLs. To that end, I've created an entity (DMCAComplaint), and a child entity (DMCAComplaintURL) which is joined to DMCAComplaint in a Doctrine OneToMany relationship.
In order to validate the URL entries via regex, I have the following assertion set up:
// src: Bundle/Event/DMCAComplaintURL.php
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255, nullable=false)
* #Assert\Regex(
* pattern="/(https?:\/\/)?([\w].)*example.com(\/.*)?/"),
* message="Please enter a URL within our site"
* )
*/
protected $url;
And in the complaint:
// src: Bundle/Entity/DMCAComplaint.php
/**
* #var \DMCAComplaintURL
*
* #ORM\OneToMany(targetEntity="DMCAComplaintURL", mappedBy="dmcaComplaint", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id", referencedColumnName="dmca_complaint_id")
* })
* #Assert\Valid
*/
protected $urls;
While the assertion works, it only gives the following error: This value is not valid. I would like it to have a custom message, as outlined in the DMCAComplaintUrl $url property. Is there a way to make this bubble up to the Valid assertion? or can I use something else to get what I need?
Set error_bubbling to true on your form field:
http://symfony.com/doc/current/reference/forms/types/text.html#error-bubbling