Sonata Doctrine Phpcr Admin Bundle: Change parent of node - sonata-admin

I have problem with changing parent of document inside sonata admin.
My document has property like this:
/**
* #PHPCRODM\Children(filter="teaserimage*", cascade="persist")
* #var Image[]
*/
protected $teaserImages;
....
/**
* #Validation\PhpcrImage(maxWidth="1500", maxHeight="844", minWidth="600", minHeight="338", minRatio="1.77", maxRatio="1.78", nullable=false)
*
* #return Image|null
*/
public function getTeaserImage()
{
return $this->getLocalizedImage($this->teaserImages, 'teaserimage');
}
/**
* #param Image $image
*/
public function setTeaserImage($image)
{
$this->teaserImages->add(
self::setupImage($image, $this->getLocalizedImageNodeName('teaserimage'), $this->getTeaserImage())
);
}
When i tried to change parent of any article document i got error like
The server returned a "500 Internal Server Error".
The Id is immutable (/cms/content/blog/blog-post-for-12th-october/teaserimage_de.jpg !== /cms/content/blog/blog-post-for-12th-october). Please use DocumentManager::move to move the document: blog post for 12th October (/cms/content/blog/blog-post-for-12th-october)
Although this error occurs, my document has been moved to the right place with all subdocument.
this is how my document looks like
https://gist.github.com/milosnovi/a83f400c8ff06b4de6dd96e1f149a8dd

Check your preUpdated, prePersists methods. You shouldn't flush object in these methods while the change is a parent change.

Related

CakePHP with PHPStan: Property does not accept Cake\ORM\Table

Working my way through the levels of PHPStan with a new applicaton, I got to level 3 and started getting error messages from all my test fixtures for models. The basic format is as follows:
------
Line tests/TestCase/Model/Table/UsersTableTest.php
------
43 Property Visualize\Test\TestCase\Model\Table\UsersTableTest::$Users (Visualize\Model\Table\UsersTable) does not accept Cake\ORM\Table.
------
The code that this error refers to is:
/**
* setUp method
*
* #return void
*/
public function setUp(): void
{
parent::setUp();
$config = $this->getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
$this->Users = $this->getTableLocator()->get('Users', $config);
}
This setup code was build using cake bake, so I'm not sure what it's looking for. Does anyone else know what will resolve this issue for me?
EDITED: I did a bit of further searching. The only version of the getTableLocator() function I could find associated with this stack was in the TableRegistry class. That class in turn has a function called get() and that function does indeed return an object of type \Cake\Orm\Table:
/**
* Get a table instance from the registry.
*
* See options specification in {#link TableLocator::get()}.
*
* #param string $alias The alias name you want to get.
* #param array $options The options you want to build the table with.
* #return \Cake\ORM\Table
* #deprecated 3.6.0 Use {#link \Cake\ORM\Locator\TableLocator::get()} instead. Will be removed in 5.0.
*/
public static function get(string $alias, array $options = []): Table
{
return static::getTableLocator()->get($alias, $options);
}
So does this mean my tests ought to expect the \Cake\ORM\Table class? TBH, I've yet to do much of anything in the way of testing Models (as you might have guessed), thus I'm not sure the consequences of doing that.
The question is how to deduce from $this->getTableLocator()->get('Users', $config); that it should be returning Visualize\Model\Table\UsersTable.
You can write a dynamic return type extension if you come up with logic that can deduce that from the abstract syntax tree and maybe other places like configuration.
It's possible that the extension https://github.com/CakeDC/cakephp-phpstan might already tackle that, this class definitely looks like that: https://github.com/CakeDC/cakephp-phpstan/blob/master/src/Type/TableLocatorDynamicReturnTypeExtension.php

Symfony: Resolve Doctrine EntityNotFoundException

I have a pre-existing Oracle database that I want to exploit.
To do this, I created 2 classes that I map with many-to-one and one-to-many. But there are elements of which the first class have no correspondence in the second class, however id exists. It does not return any error.
I would like that it sends me rather a null. How to do that ?
class Fact
{
/**
*
* #ORM\OneToMany(targetEntity="present\UserBundle\Entity\March",mappedBy="fact", cascade={"persist","remove"})
*/
private $march;
}
class March
{
...
/*
* #var \present\PublishBundle\Entity\image
* #ORM\ManyToOne(targetEntity="present\UserBundle\Entity\fact",inversedBy="march", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="id",referencedColumnName="id")
* })
* })
*/
private $facture;
}
the error
Entity of type 'present\UserBundle\Entity\Client' for IDs id(0) was not found
Thanx you
I haven't tested your code but I think your error has to do your JoinColumn declaration :
#ORM\JoinColumn(name="id",referencedColumnName="id")
In the documentation here it says that the name attribute will define the name of the column for your relation.
But here "id" is also referencing to your primary key I suppose.
To solve it, try to change the name="id" by name="march_id", or remove the JoinColumn.
Edit : I have read a bit to quickly the error, haven't seen it's referencing to Client entity, can you show the code related to this relation too ?

how to inject a variable in a OneToOne relation entity

I have a File entity for handle files upload in other entities (news/blog/etc).
I point to it with a OneToOne relation and it works fine. But I would change the upload dir, for each relation entity :
upload/news
upload/blog
The upload path is set in my file entity so i dont know how to automaticaly update the path foreach relations...
do you have an idea on how to do it ?
Thanks
Of course you can do it.
On your file entity side, you can add a uploadDir attribute, and create a setter like this :
private $uploadDir;
public function setUploadDir($uploadDir)
{
if (!$this->uploadDir) {
$this->uploadDir = $uploadDir;
}
}
your comment suggest you use Symfony with Doctrine right ?
So you can edit the classical getUploadDir() method like this:
protected function getUploadDir()
{
return 'uploads/' . $this->uploadDir;
}
In the 'parent' entity you have to update this attribute (when it is created) before persist or update.
(I personally use life cycle callbacks but you can do it manually in your controller)
use Doctrine\ORM\Mapping as ORM;
/**
* News
*
* #ORM\Table(name="News")
* #ORM\HasLifecycleCallbacks
*/
class News
{
//....
/**
* #ORM\OneToOne(targetEntity="File",cascade={"persist","remove"})
*/
private $file;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
function setUploadDir()
{
$this->getFile()->setUploadDir('news');
}
// ....
You can also add a constant to make the code cleaner...
I hope it'll help you

Laravel 5.0 -Dev Defining Global Patterns is not working

Laravel Docs provide a way to add global patterns inside the before function inside RouteServiceProvider.php.
my question is : by default there is no such function, besides, after creating it, it's not working!
/**
* Define global rules for routes.
* more specially for regullar expressions.
*
* #param \Illuminate\Routing\Router $router
* #return void
*/
public function before(Router $router){
$router->pattern('id', '[1-9]+[0-9]*');
}
Just in case you're still interested (I dont have enough points to comment), you can still declare it inside boot() but just before the $route variable is passed to the parent parent::boot($router); function, like here:
/**
* Define your route model bindings, pattern filters, etc.
*
* #param \Illuminate\Routing\Router $router
* #return void
*/
public function boot(Router $router)
{
///////////////////////////
// route global patterns //
///////////////////////////
$router->pattern('id', '[0-9]+');
parent::boot($router);
}
This worked for me
I had the same problem and what I did was to append the before() method body into the map() method body. It worked for me. :)

Symfony2 file upload step by step

I am still learning Symfony2 and don't understand how to upload a file.
Don't worry, I've already checked the documentation. It's really good, but my problem isn't explained in any tutorial.
I am looking for guidance on how to upload a file with Symfony2 but with all the thing everybody needs (such as constraint of extension, rename of the file based on the id and stuff, store the path in the db, etc...)
I found good tutorials, tried to mixed them but with no success. Each time a different problem appears: file re-uploads on every submit on the form (even if the file field is empty), guessExtension impossible to used, tmp path stored in the database instead of the right path, file not moved, impossible to used the id in the rename because the id is on auto-increment and so not yet generated).
So, I'll put an 'standard' entity, let say: Photo.php
/**
* Photo
*
* #ORM\Table(name="photo")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Photo
{
// Annotation for the id and auto increment etc
private $id;
/**
* #var string
* #Assert\File( maxSize = "3072k", mimeTypesMessage = "Please upload a valid Image")
* #ORM\Column(name="image", type="string", length=245, nullable=false)
*/
private $image
private $title
private $description
// all the function get, set for the 4 previous variables
}
and the controller:
public function addPhotoAction()
{
$add_photo = new Photo;
$formBuilderPhoto = $this->createFormBuilder($add_photo);
$formBuilderPhoto
->add('title','text',array('label' => 'Title of the photo', 'required' => true))
->add('image','file', array('required' => true, 'data_class' => null))
->add('description','textarea',array('label' => 'Description of your photo', 'required' => false))
;
$form_photo = $formBuilderPhoto->getForm();
if ($request->getMethod() == 'POST') {
$form_photo->bind($request);
if ($form_photo->isValid()) {
// ...
}
}
return $this->render('MyBundle:frontend:photo.html.twig',
array('form_photo' => $form_photo->createView())
);
}
Do you know now what are the 'important' function to add to be able to upload the photo and rename it ?
How do you check the extension to see if the upload is possible?
What is your actual way of doing such a thing with Symfony2? I know there is a lot of Bundle that do all those things for you but I want to learn to do it and understand the process.
What is the 'classic' way to implement a file upload form and rename function with Symfony2?
Do you know now what are the 'important' function to add to be able to upload the photo and rename it?
See the official documentation on how to do this. There are good working examples for a simple file upload. Also check the doctrine documentation for lifecycle callbacks.
How do you check the extension to see if the upload is possible?
There is some HTML-Form validation in each browser. See this question for the HTML accept="" attribute in input elements. Also in Symfony2 you can specify the MIME-type of an uploaded file using this annotation:
/**
* #Assert\File(
* maxSize = "1024k",
* mimeTypes = {"application/pdf", "application/x-pdf"},
* mimeTypesMessage = "Please upload a valid PDF"
* )
*/
Even though you did not want to use any bundles I'll have to recommend you the KnpDoctrineBehavioursBundle which makes file uploading way easier.
Step-by-step:
Because you read the documentation already I'll give you a step by step code-example.
First of all you need an entity. Let's call it Image:
/**
* Class Image
*
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks
*/
class Image extends BaseEntity
{
Note the #ORM\HasLifecycleCallbacks annotation. It is very important and you need it later. We create all the basic fields like ID and what not. Also we need a field to store the file path in:
/**
* Image path
*
* #var string
*
* #ORM\Column(type="text", length=255, nullable=false)
*/
protected $path;
And one for the Image itself. Here we also define the Validation for the images. In my example it has to be 5M big and of one of the defined mimeTypes. It should be self-explanatory. Otherwise the official docs help as always.
/**
* Image file
*
* #var File
*
* #Assert\File(
* maxSize = "5M",
* mimeTypes = {"image/jpeg", "image/gif", "image/png", "image/tiff"},
* maxSizeMessage = "The maxmimum allowed file size is 5MB.",
* mimeTypesMessage = "Only the filetypes image are allowed."
* )
*/
protected $file;
Add all the Getters & Setters and update your database schema with this command:
php app/console doctrine:schema:update --force
Next we need the lifecycles. They are methods in the Entity that are called on certain events. For example the #ORM\PreUpdate() annotation before a method says that this method is being called right before the entity gets updated.
/**
* Called before saving the entity
*
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename.'.'.$this->file->guessExtension();
}
}
Before the entity gets stored or updated this method is called. You can use it to e.g. generate a unique file name.
/**
* Called before entity removal
*
* #ORM\PreRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
Called before the entity gets removed. This gives you time to delete the image from your folders or log a message if you want to.
/**
* Called after entity persistence
*
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
// The file property can be empty if the field is not required
if (null === $this->file) {
return;
}
// Use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->file->move(
$this->getUploadRootDir(),
$this->path
);
// Set the path property to the filename where you've saved the file
//$this->path = $this->file->getClientOriginalName();
// Clean up the file property as you won't need it anymore
$this->file = null;
}
This is the important part where your file is actually moved to the right directory. Note that I have used some additional methods. You can all get them from the official docs.
Next thing you need is a form. The form class itself is very simple. Just make sure you set the default data_class like this:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'FSchubert\SiyabongaBundle\Entity\Image',
)
);
}
A file upload field can be created very easily in the buildForm() method:
$builder->add('file', 'file');
The methods for your Controller are a little long for just pasting them here and IMHO it's not part of answering your question. There are countless examples out there for writing a proper Controller Action for your purpose.
More things you have to keep in mind:
You need to give your app writing permissions to the folders you upload the files to. Although it seems obvious it can be annoying if you have multiple servers you run the application on.
There is an Image Constraint for your entity as well. You can find it here. But since you were talking about a file upload I used the File Constraint instead.
As I mentioned in the top of this post, there are many Bundles that handle all these things for you. Check them out if you want an easy life.
Edit:
Changed from DoctrineExtensionsBundle to DoctrineBehaviours since development on the old one stopped in favour of the DoctrineBehaviours bundle.
I recommend you to use vlabs media bundle.
The VichUploaderBundle is also easy to use for uploading files:
https://github.com/dustin10/VichUploaderBundle
I recommend VichUploader bundle and this code with the bundle implanted in the entity and the FormType.
composer require vich/uploader-bundle
Admission.php
/**
* #ORM\Entity(repositoryClass=AdmissionRepository::class)
* #Vich\Uploadable
*/
class Admission
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $cin;
/**
* #Vich\UploadableField(mapping="product_image", fileNameProperty="cin")
* #var File
*/
private $cinFile;
public function getId(): ?int
{
return $this->id;
}
public function getCin(): ?string
{
return $this->cin;
}
public function setCin(string $cin): self
{
$this->cin = $cin;
return $this;
}
}
AdmissionType.php
class AdmissionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('cinFile', VichFileType::class);
}
vich_uploader.yaml
vich_uploader:
db_driver: orm
mappings:
product_image:
uri_prefix: /uploads
upload_destination: '%kernel.project_dir%/public/uploads'
inject_on_load: false
delete_on_update: true
delete_on_remove: true
namer: vich_uploader.namer_origname

Resources