Symfony2 && mongodb simple reference - mongodb

he guys,
i want to make a simple reference on mongodb documents using symfony2.
i have this two documents and want to store picture references into the requests document. it works for me, if i have only the picture ids in the requests document.
so i need the follow:
can everyone change the document files and make and extends the custum call to get all pictures as object from the requests (picture array)?
my original files:
Document Pictures:
<?php
namespace TestBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="TestBundle\Repository\RequestsRepository")
*/
class Requests
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $title;
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
}
Document Pictures:
<?php
namespace TestBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document(repositoryClass="TestBundle\Repository\PicturesRepository")
*/
class Pictures
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\String
*/
protected $filename;
public function setId($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
public function setFilename($filename)
{
$this->filename = $filename;
}
public function getTitle()
{
return $this->filename;
}
}
My Basic Calls:
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm->getRepository('TestBundle:Requests')->find($requestId);
To my tests:
i added in the requests document the follow:
/**
* #MongoDB\ReferenceMany(targetDocument="Pictures",cascade={"persist"},simple="true")
*/
protected $pictures = array();
public function setPictures($pictures)
{
$this->pictures[] = $pictures;
}
public function getPictures()
{
return $this->pictures;
}
and added pictures like this:
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$photo = $dm->getRepository('TestBundle:Pictures')->find($photoId);
$dm1 = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm1->getRepository('TestBundle:Requests')->find($requestId);
$request->setPictures($photo);
$dm1->flush();
this works - but i cannot get the pictures by loading the document.
my code to load:
$dm1 = $this->get('doctrine.odm.mongodb.document_manager');
$request = $dm1->getRepository('TestBundle:Requests')->find($requestId);
$pictures = $request->getPictures();
foreach($pictures as $picture)
{
print $picture->getId();
}
THIS WILL NOT WORK. i become the follow error:
Fatal error: Doctrine\ODM\MongoDB\Proxy\ProxyFactory::getProxy():
Failed opening required
'.../app/cache/dev/doctrine/odm/mongodb/Proxies/_CG_TestBundleDocumentPictures.php'
(include_path='.:.../library:/usr/local/zend/share/pear') in
..../test/vendor/doctrine-mongodb-odm/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php
on line 100
thanks, jan

First off you only need to call doctrine one time in $dm your overloading your resources and thats bad practice. One function, one Doctrine call. Secondly, you need a $dm->persist($request) and then $dm->flush(). Create a OnetoOne between your Documents and then make $pictures an Doctrine Array Collection. Then set a picture like you tried, then make a smiple query and call $request->getPicture()->getId().

Ok i found the error:
In the deps file i have the following lines:
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.1.4
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.1.7
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.1.7
After updating them to:
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.2.1
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.2.1
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.2.1
And doing php bin/vendors update the references will work again

Related

Unable to generate an IRI when using mongoDb

I'm new to the API Platform framework, and for the project I'm working on we're required to use mongoDb.
I followed the instructions on how to set it up with mongoDb (see here). But I installed doctrine/mongodb-odm:2.0.x.-dev instead of doctrine/mongodb-odm:^2.0.0#beta so that the bin/console doctrine:mongodb:schema:create command works...
In any case, I created a very simple document based off the provided example:
<?php
// api/src/Document/Test.php
namespace App\Document;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ApiResource
*
* #ODM\Document
*/
class Test
{
/**
* #ODM\Id(strategy="INCREMENT", type="integer")
*/
private $id;
/**
* #ODM\Field(type="string")
* #Assert\NotBlank
*/
private $name;
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
}
However, the response I get is:
"Unable to generate an IRI for the item of type \"App\\Document\\Test\"". But as you can see the ID-getter function is there, which seems the be the "usual" cause of the error.
The full Response Body can be found in this pastebin.
What am I missing?
Thanks!
An update to API-Platform 2.4.0-beta has solved the issue

Handle filesUpload in a single table Symfony

Thank you for your interest,
SHORT
I want to manage all my uploads (Image, PDF, Video etc...) in a single entity, so I use entity Inheritance to get various "types" and OneToOne relations to link parent entity with correct upload. I didn't found any bundle to do this and face problems:
Constraints use
Setting uploaded file and not upload entity
Get uploaded file and not upload entity (edition)
LONG
Instead of having 1 file management in each table (which is quiet verbose) I preferred to have only one table Uploads to handle every Uploads. Then I just have to do OneToOne relations to get my file, plus using inheritance I can apply various treatment depending on Image or PDF for example.
I have at least 4 entities that needs image, so I think that 1to1 relation is a good choice.
But I face problems doing things like this :
Constraints aren't taking into account
Edition of $file should set $file->file (it doesn't send the entity from Uploads/Image but the file to create this entity
The Uploaded file isn't loaded on entity edition and should be reuploaded each time I edit entity
Does anyone did this ? I can't find out how to achieve this correctly.
Looking at the assert problem I tried to:
Define asserts on Image (this doesn't work as expected as the form target the $file of WithImage)
Using annotation #Assert\Image()
Using loadValidatorMetadata
Using annotation #Assert\Callback()
Define assert on form field 'constraints' => array(new Assert\Image()), this works but need to be defined everywhere I use it...
Looking at the setter misused I found a workaround, but this is quiet ugly:
public function setFile($file = null)
{
if ($file instanceof \Symfony\Component\HttpFoundation\File\UploadedFile) {
$tmpfile = new Image();
$tmpfile->setFile($file);
$file = $tmpfile;
}
$this->file = $file;
return $this;
}
(PS: I read about traits to avoid copy/paste of code, I have checked the SonataMediaBundle but this doesn't seems to apply to my case)
CODE
So I designed my classes as follow:
Entity\Uploads.php To handle all the life of a file from upload to remove (and access, move, edit, possibly thumbnail etc ...)
<?php
namespace Acme\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Acme\CoreBundle\Utils\UUID;
/**
* Uploads
*
* #ORM\Table(name="uploads")
* #ORM\Entity(repositoryClass="Acme\CoreBundle\Repository\UploadsRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="class", type="string")
* #ORM\DiscriminatorMap({"image" = "Image"})
* #ORM\HasLifecycleCallbacks
*/
abstract class Uploads
{
protected $file;
private $tempFileName;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="fileName", type="string", length=36, unique=true)
*/
private $fileName; // UUID
/**
* #var string
*
* #ORM\Column(name="extension", type="string", length=4)
*/
private $extension;
/**
* Get id.
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set date.
*
* #param \DateTime $date
*
* #return uploads
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date.
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set fileName.
*
* #param string $fileName
*
* #return uploads
*/
public function setFileName($fileName)
{
$this->fileName = $fileName;
return $this;
}
/**
* Get fileName.
*
* #return string
*/
public function getFileName()
{
return $this->fileName;
}
/**
* Set extension
*
* #param string $extension
*
* #return string
*/
public function setExtension($extension)
{
$this->extension = $extension;
return $this;
}
/**
* Get extension
*
* #return string
*/
public function getExtension()
{
return $this->extension;
}
public function getFileNameExt()
{
return $this->getFileName().'.'.$this->getExtension();
}
public function setFile(UploadedFile $file)
{
$this->file = $file;
if (null !== $this->getId()) {
$this->tempFileName = $this->getFileNameExt();
$this->fileName = null;
$this->extension = null;
}
}
public function getFile()
{
return $this->file;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null === $this->file) {
return;
}
$this->extension = $this->file->guessExtension();
$this->fileName = UUID::v4();
$this->preUpdateFile();
}
protected function preUpdateFile(){} // To define if specific treatment
/**
* #ORM\PrePersist()
*/
public function prePersistDate()
{
$this->date = new \DateTime();
return $this;
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
if (null !== $this->tempFileName) {
$oldFile = $this->getUploadRootDir().$this->tempFileName;
if (file_exists($oldFile)) {
unlink($oldFile);
}
}
$this->file = $this->file->move(
$this->getUploadRootDir(),
$this->getFileNameExt()
);
$this->postUpdateFile();
}
protected function postUpdateFile(){} // To define if specific treatment
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
// On sauvegarde temporairement le nom du fichier
$this->tempFileName = $this->getFileNameExt();
$this->preRemoveFile();
}
protected function preRemoveFile(){} // To define if specific treatment
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
$oldFile = $this->getUploadRootDir().$this->tempFileName;
if (file_exists($oldFile)) {
unlink($oldFile);
}
$this->postRemoveFile();
}
protected function postRemoveFile(){} // To define if specific treatment
public function getFileUri()
{
return $this->getUploadDir().$this->getFileNameExt();
}
public function getUploadDir()
{
return 'uploads/';
}
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
public function __toString() {
return $this->getFileNameExt();
}
}
Entity\Image.php A specific type of upload with its own constraints and file management
<?php
namespace Acme\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Image
*
* #ORM\Entity(repositoryClass="Acme\CoreBundle\Repository\ImageRepository")
*/
class Image extends Uploads
{
}
Entity\WithImage.php An entity which needs an Image
<?php
namespace Acme\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* WithImage
*
* #ORM\Table(name="with_image")
* #ORM\Entity(repositoryClass="Acme\CoreBundle\Repository\WithImageRepository")
*/
class WithImage
{
/**
* #ORM\OneToOne(targetEntity="Acme\CoreBundle\Entity\Image", cascade={"persist", "remove"})
*/
protected $file;
}
Some thoughts come to my mind to help you achieve what you want.
First, you have to upload the files in a form, and the constraints should be in a property in an entity (unless you want to have the pain of writing your constraints in every form, which is not very mantainable). So, for every entity that's going to have files, define a file property (not ORM anotated) and write your constraints there. Also add the respective getters and setters.
/**
* #var UploadedFile
* #Assert\NotBlank(groups={"New"})
* #Assert\File(mimeTypes={"text/html", "text/markdown", "text/plain"})
*/
private $file;
Second, you might ask ¿but how do I save them to a different Entity? This is when I would recommend you to use a Doctrine Event Subscriber. Basically, is a service that is defined with a doctrine.event_subscriber tag which class implements the Doctrine\Common\EventSubscriber Interface. You can subscribe to events like preUpdate, postLoad, and the interesting for you: prePersist.
My take on this would be that you subscribe to the prePersist event. The event will pass you the entity (with that file non-orm property we created, that has the UploadedFile instance holding your file info).
Then, using the info for that file, create a new Upload entity, pass all the info you want, and then set that in the real file orm mapped property that holds your file relationship with your desired entity. For this to work you will have to enable persist cascade if I recall correctly.
The benefits of this:
1. You can define your constraints in your Entities.
2. You can have the Uploads Entity you desire.
The only major problem is that you will have to do all the retrieval, storing and updating of the Uploads entity through a listener. But's the only thing I can think of to help you.

How to get limited number of fields from embedded document with mongodb-odm?

I want to get the result of this mongodb command with Doctrine MongoDB ODM:
db.posts.find({_id: 3}, {comments: {$slice: [0, 5]}})
I was looking for it, but didn't find anything, neither in the documentation. If you have any idea, please help, thank you.
Here are my document classes:
/** #ODM\Document(db="dbname" collection="posts") */
class Posts{
/** #ODM\Id */
private $_id;
/** #ODM\Field(type="string") */
private $some_field;
/** #ODM\EmbedMany(targetDocument="Comment") */
private $comments = array();
public function getSomeField() { return $this->some_field; }
public function setSomeField($content) { $this->some_field = $content; }
public function getComments() { return $this->comments; }
public function addComment(Comment $comment) { $this->comments[] = $comment; }
}
/** #ODM\EmbeddedDocument */
class Comment {
/** #ODM\Field(type="string") */
private $timeStamp;
/** #ODM\Field(type="string") */
private $message;
/** #ODM\ReferenceOne(targetDocument="Users", simple=true) */
private $user_ref;
public function getTimeStamp() { return $this->timeStamp; }
public function setTimeStamp($str) { $this->timeStamp = $str;}
public function getMessage() { return $this->message; }
public function setMessage($str) { $this->message = $str; }
public function getUser() { return $this->user_ref; }
public function setUser(Users $user) { $this->user_ref = $user; }
}
While documentation on query builder's ->select() doesn't go much into details nor methods alike, following code will allow you to run your command:
$qb = $this->dm->createQueryBuilder(Posts::class)
->selectSlice('comments', 0, 5)
->field('_id')->equals(3);
$query = $qb->getQuery();
$results = $query->execute();

MongoDB ODM - Group by MAX(group_id)

Let me get to the point, I am currently using the Doctrine MongoDB ODM in conjunction with Symfony2 to persist data into MongoDB.
Currently I am grouping my results by type, but I would like to group them by MAX(group_id) as well.
Sure I can just alter the reduce function, but I am trying to steer clear of a large return array and more processing once the query is done, so I was wondering if there is a more elegant solution than that to this particular problem.
The Monitoring document,
/**
* #ODM\Document(collection="monitoring")
*/
class Monitoring
{
/** #ODM\Id */
public $id;
/** #ODM\String */
public $type;
/** #ODM\String */
public $message;
/** #ODM\Int */
public $groupId;
.... getters and setter etc ....
}
MonitoringManager function to fetch all items,
public function getAllMonitoringItems(){
return $this->dm->createQueryBuilder('MonitoringBundle:Monitoring')
->group(array(), array('groups' => array()))
->reduce('function (obj, prev) {
var type = obj.type;
if(!prev.groups.hasOwnProperty(type)){
prev["groups"][type] = [];
prev["groups"][type].push(obj);
} else {
prev["groups"][type].push(obj);
}
}')
->field('type')->notIn(array("graph"))
->getQuery()
->execute()
->toArray();
}

Annotations Namespace not loaded DoctrineMongoODMModule for Zend Framework 2

I've loaded up the Doctrine MongoODM Module for zf2. I have got the document manager inside my controller, and all was going well until I tried to persist a document. It fails with this error:
"[Semantical Error] The annotation "#Document" in class SdsCore\Document\User was never imported."
It seems to fail on this line of DocParser.php
if ('\\' !== $name[0] && !$this->classExists($name)) {
It fails because $name = 'Document', and the imported annotation class is 'Doctrine\ODM\MongoDB\Mapping\Annotations\Doctrine'
Here is my document class:
namespace SdsCore\Document;
/** #Document */
class User
{
/**
* #Id(strategy="UUID")
*/
private $id;
/**
* #Field(type="string")
*/
private $name;
/**
* #Field(type="string")
*/
private $firstname;
public function get($property)
{
$method = 'get'.ucfirst($property);
if (method_exists($this, $method))
{
return $this->$method();
} else {
$propertyName = $property;
return $this->$propertyName;
}
}
public function set($property, $value)
{
$method = 'set'.ucfirst($property);
if (method_exists($this, $method))
{
$this->$method($value);
} else {
$propertyName = $property;
$this->$propertyName = $value;
}
}
}
Here is my action controller:
public function indexAction()
{
$dm = $this->documentManager;
$user = new User();
$user->set('name', 'testname');
$user->set('firstname', 'testfirstname');
$dm->persist($user);
$dm->flush;
return new ViewModel();
}
I didn't yet work on the DoctrineMongoODMModule, but I'll get to it next week. Anyway, you are still using the "old way" of loading annotations. Most of the doctrine projects are now using Doctrine\Common\Annotations\AnnotationReader, while your #AnnotationName tells me that you were using the Doctrine\Common\Annotations\SimpeAnnotationReader. You can read more about it at the Doctrine\Common documentation
So here's how to fix your document:
<?php
namespace SdsCore\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/** #ODM\Document */
class User
{
/**
* #ODM\Id(strategy="UUID")
*/
private $id;
/**
* #ODM\Field(type="string")
*/
private $name;
/**
* #ODM\Field(type="string")
*/
private $firstname;
/* etc */
}