Ordering embedded documents with Doctrine ODM - mongodb

I’m trying to order my embed documents.
The field looks like this
/**
* #ODM\EmbedMany(targetDocument=Image::class, strategy="set")
* #ODM\Index(keys={"order"="asc"})
* #Groups({"offer:read"})
*/
protected $images = [];
The Image EmbeddedDocument
namespace App\Document\Embedded;
use App\Document\Traits\NameableTrait;
use App\Document\Traits\OrderableTrait;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* #ODM\EmbeddedDocument
*/
class Image
{
use NameableTrait;
use OrderableTrait;
…
}
And the orderable trait
namespace App\Document\Traits;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Serializer\Annotation\Groups;
trait OrderableTrait
{
/**
* #ODM\Field(type="int")
* #Groups({"offer:read"})
*
* #var int|null
*/
private $order;
public function getOrder(): int
{
return $this->order;
}
public function setOrder(int $order): void
{
$this->order = $order;
}
}
I updated the indexes with bin/console doctrine:mongodb:schema:update
However my Images are not ordered. Are the indexes the way to do it?

Index is not used for ordering documents in any way, they are telling the database to index the data so it can be searched through efficiently. In Doctrine's ORM there is an #OrderBy annotation but sadly it has not made its way to the ODM (yet). The solution would either be to support such thing natively in the ODM itself or you could use a custom collection for your embedded documents.
Here you will find documentation for custom collections and here's a link to my package that aims to kickstart your own implementations: https://github.com/malarzm/collections. To get what you need you will need your own collection class looking somehow like this:
class SortableCollection extends Malarzm\Collections\SortedCollection
{
public function compare($a, $b)
{
return $a->getOrder() <=> $b->getOrder();
}
}
and then plug it into your mapping:
/**
* #ODM\EmbedMany(targetDocument=Image::class, strategy="set", customCollection=SortableCollection::class)
* #ODM\Index(keys={"order"="asc"})
* #Groups({"offer:read"})
*/
protected $images = [];

Related

Is it possible to disable lazy loading for ODM Doctrine?

We are developing API with Silex and Doctrine (ODM) and we have object Story, which have property images.
class Story extends AbstractDocument
{
/** #MongoDB\Id */
protected $id;
/**
* #MongoDB\ReferenceMany(
* targetDocument="MyNamespace\Documents\Image",
* storeAs="DBRef"
* )
*/
protected $images = [];
// Other properties and methods
}
We have get method in repository (in AbstractRepository, from which extends all other repositories).
public function get(string $documentId) : array
{
$document = $this->createQueryBuilder()
->field('id')->equals($documentId)
->hydrate(false)
->getQuery()
->toArray();
}
This method returns embedded and referenced objects, but for referenceMany returns only ids without data.
Is it possible to deny lazy loading to get all documents ?
One possible solution, which we found - rewrite method toArray.
As soon as you use ->hydrate(false) you are instructing ODM to get out of your way and return you raw data from MongoDB. You are seeing the referenceMany as an array of ids because that is the raw representation, no lazy loading is involved.
The cleanest way to solve your issue would be implementing StoryRepository which would fire an additional query to get referenced images:
public function get(string $documentId) : array
{
$document = $this->createQueryBuilder()
->field('id')->equals($documentId)
->hydrate(false)
->getQuery()
->toArray();
$document['images'] = /* ... */;
return $document;
}

Doctrine and MongoDb: Define required fields in MongoDb-Document (

I'm using MongoDB with Doctrine for a Symfony project.
For a document (I call it class Product) I need to define a field (e.g. name), but I want to act doctrine in a way that the document cannot be saved without defining a it. Meaning, whenever creating or updating a new Product, the property $name has to be defined.
For doctrine and MySql this is quite simple. There you have to say:
class Product {
...
/** #ORM\Column(type="string", nullable=false) */
private $name;
...
}
Is there a similar way in MongoDb?
The following code (which I found in a similar thread) doesn't work:
class Product {
...
/**
* #MongoDB\Field(type="string")
* #Constraints\NotBlank
*/
protected $name;
...
}

How to get the type of MongoDB Document in twig?

I used Symfony2 and Doctrine MongoDBBundle and I have simple Single Collection Inheritance classes. How can I know what type of document it is in a twig template? For example the base class is Entity and extended by User and Organization, in listing those in a twig template I'd like to know what type of entity it is (i.e. whether it's a User or an Organization). I wonder if it's possible to get the value of the DiscriminatorField of the document.
/**
* #MongoDB\Document(collection="entity")
* #MongoDB\InheritanceType("SINGLE_COLLECTION")
* #MongoDB\DiscriminatorField(fieldName="type")
* #MongoDB\DiscriminatorMap({"user"="User", "shop"="Shop"})
*/
class Entity
{
/**
* #MongoDB\Id
*/
protected $id;
protected $entityType;
public function getEntityType()
{
return $this->entityType;
}
}

How do I use my own data as the ID for a Doctrine MongoDB mapped document instead of the automatically generated Object ID?

The MongoDB documentation on Object IDs recommends using custom keys in a certain case:
If your document has a natural primary key that is immutable we recommend you use that in _id instead of the automatically generated ids.
How can I define a simple model object that does exactly this?
<?php
namespace Acme\HelloWorld\Model;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class KindWord {
/**
* #MongoDB\Id(strategy="NONE")
* #var string
*/
private $word;
public function getWord() {
return $this->word;
}
public function setWord($word) {
$this->word = $word;
}
}
Just make sure you set $word before calling persist().

is it possible to override doctrine2 persistentobject magic getters and setting

Can anybody tell me whether its posible to override doctrine2 persistentobject magic getters\setters? i'd like to do the below:-
public function setDob($dob)
{
$this->dob= new \Date($date);
}
however my entity is defined as:-
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\ORM\Mapping as ORM;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Ajfit\Repository\User")
* #ORM\HasLifecycleCallbacks
*/
class User extends \Doctrine\Common\Persistence\PersistentObject
{
/**
* #var date $dob
*
* #ORM\Column(name="dob", type="date")
*/
protected $dob;
}
the public function setDob does not get called when I create the entity using:-
public function getNewRecord() {
return $this->metadata->newInstance();
}
I get the below error:-
Notice:- array to string conversion ...Doctrine\DBAL\Statement.php on line 98
Any help would be much apprieciated.
Thanks
Andrew
__call of PersistentObject#__call will not be called if you defined the setDob method.
What you're doing there is creating a new instance via metadata. What you are doing there is probably assuming that __construct or any setter/getter should be called by the ORM. Doctrine avoids to call any methods on your object when generating it via metadata/hydration (check ClassMetadataInfo#newInstance to see how it is done) as it does only know it's fields.
This allows you to be completely independent from Doctrine's logic.
About the notice, that is a completely different issue coming from Doctrine\DBAL\Statement, which suggests me that you have probably some wrong parameter binding in a query. That should be handled separately.