symfony2 entity customized function: call to undefined function error - symfony

I am trying to add 3 functions for slugifying non-ascii characters in the url, but my slugify function doesn't recognize the sanitize() function although they are in the same scope.:|
class Blog
{
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="blog")
*/
protected $comments;
...
public function slugify($text)
{
return sanitize($text);
}
...
public function sanitize($title)
{
$title = strip_tags($title);
...
}
...
}
I can't load the fixtures and I get an exception which says "call to undefined function ..\sanitize() in ..\Entity\Blog.php ..."
why this happens? i even tried these: clearing the cache, dropping the database, recreating database, recreating schemas, recreating entities, reloading the fixtures...
but all I've got is the same error:(
can anyone tell me what's wrong with it?

Missing the $this from your return statement. When calling sanitize() without $this it's resolving to the method scope and not the class scope.
class Blog
{
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="blog")
*/
protected $comments;
...
public function slugify($text)
{
return $this->sanitize($text);
}
...
public function sanitize($title)
{
$title = strip_tags($title);
...
}
...
}

Related

Symfony 5 object not found by the #ParamConverter annotation and 404 redirection

I want to redirect users to a 404 page.
My problem is that if the slug does not exist I get an error:
App\Entity\Article object not found by the #ParamConverter annotation.
Unfortunately I can't find a way to get the NULL for the condition.
/**
* #Route("/rubriques/{slugCategory}/{slug}", name="articleSingle")
*/
public function show(Article $article, String $slug): Response
{
$article = $this->repoArticle->findOneBy(['slug' => $slug]);
dd($article);
if (!$article) {
return $this->redirectToRoute('404error');
}
return $this->render("show/index.html.twig", [
'article' => $article,
]);
}
You don't need to set "Article" as your controller dependency as you don't want to use the DoctrineParamConverter to automatically find the object for you. So you should change
public function show(Article $article, String $slug): Response
{
...
}
To
public function show(string $slug): Response
{
...
}
That way, you will be able to manually find the article corresponding to the passed slug and if there is no result, you will be able to redirect the user to a 404 page.
Why dont you use this:
If the field in Article is slug you can use it directly:
/**
* #Route("/rubriques/{slugCategory}/{slug}", name="articleSingle")
*/
public function show(Article $article): Response
{
...
}
see https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html#fetch-automatically
If the field in Article is different:
/**
* #Route("/rubriques/{slugCategory}/{slug}", name="articleSingle")
* #Entity("article", expr="repository.findOneBySlug(slug)")
*/
public function show(Article $article): Response
{
...
}
where findOneBySlug has to be adepted to the correct method in the repository
see https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html#fetch-via-an-expression

Using Typed Properties (PHP 7.4) with Symfony forms

I use DTOs as the data_class for Symfony form types. There is one thing that does not work for me when I use typed properties (PHP 7.4) in these DTOs.
EXAMPLE:
class ProductDto
{
/*
* #Assert\NotBlank
*/
public string $title;
}
This generally seems to work quite well – in case the user submits the form with a blank title or description, the validation kicks in and the form is displayed with validation warnings.
BUT THERE IS A PROBLEM when data is added while creating a form (e.g. the edit form):
$productDto = new ProductDto();
$productDto->title = 'Foo';
$form = $this->createForm(ProductFormType::class, $productDto);
Initially the form is displayed as expected with Foo as the value for the title. When a user clears the title input form field and submits the form an exception like this is thrown:
Typed property `App\Form\Dto\ProductDto::$title` must be string, null used
As far as I can see this is caused by the fact that during Form->handleRequest() the title is set to null after it was set to "Foo" before, right?
Is there a solution for this problem?
Since PHP 7.4 introduces type-hinting for properties, it is particularly important to provide valid values for all properties, so that all properties have values that match their declared types.
A property that has never been assigned doesn't have a null value, but it is on an undefined state, which will never match any declared type. undefined !== null.
Here is an example:
<?php
class Foo
{
private int $id;
private ?string $val;
public function __construct(int $id)
{
$this->id = $id;
}
}
For the code above, if you did:
<?php
foo = new Foo(1);
$foo->getVal();
You would get:
Fatal error: Uncaught Error: Typed property Foo::$val must not be
accessed before initialization
See this post for more details https://stackoverflow.com/a/59265626/3794075
and see this bug https://bugs.php.net/bug.php?id=79620
This is what I just came up with:
DTO:
use GenericSetterTrait;
/**
* #Assert\NotBlank
*/
public string $title;
public function setTitle(?string $title): void
{
$this->set('title', $title);
}
/**
* #Assert\NotNull
*/
public Foo $foo;
public function setFoo(?Foo $foo): void
{
$this->set('foo', $foo);
}
Trait:
trait GenericSetterTrait
{
private function set(string $propertyName, $value): void
{
if ($value === null) {
unset($this->{$propertyName});
} else {
$this->{$propertyName} = $value;
}
}
}
Seems to work. What do you think? Any objections?

retrieve attribute from object in entity doctrine

So I have this function :
MyProject\Bundle\Entity\Audio;
/**
*
* #return string
*/
public function getStudioName()
{
return $this->getStudio()->getNom();
}
which is supposed to retrieve the attribute nom from the object Studio.
They are defined like this :
/**
* #var \MyProject\Bundle\Entity\Device
*/
private $studio;
...
/**
* Get studio
*
* #return \MyProject\Bundle\Entity\Device
*/
public function getStudio()
{
return $this->studio;
}
And the ->getNom is just also a basic return, which works fine.
So i get that following error message :
Error: Call to a member function getNom() on a non-object
I've read about lazy loading and I understand why $this->getStudio() gives me a proxy instead of an actual Device object, but I can't go further and use getNom() after that...
I've tried to add fetch : EAGER to avoid lazy loading but it still doesn't work.
Any ideas ?
It looks like property $studio can be NULL. In such a case, you need to validate if it is set. If not, return NULL.
The real code would look like this:
<?php
public function getStudioName(): ?string
{
return $this->studio ? $this->studio->getName() : null;
}

Is it possible to return mutiple values with #depends?

The #depends annotation allows to express dependencies between tests:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return $client;
}
/**
* #depends testOne
*/
public function testTwo(Client $client)
{
// ...
}
}
If I want to return several values, I can return an array of values, such as:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return array($client, $node);
}
/**
* #depends testOne
*/
public function testTwo(array $items)
{
list ($client, $node) = $items;
// ...
}
}
While it works fine, the problem with this approach is that I lose the type hinting of my IDE, and would have to manually annotate the $client and $node variables so that it understands them properly.
What I'd like to do instead, is to explicitly use the return values as separate parameters in the second test:
class MyTest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
// ...
return array($client, $node);
}
/**
* #depends testOne
*/
public function testTwo(Client $client, Node $node)
{
// ...
}
}
Is that possible?
I looked a while back and I think the answer is no. This is more of a clarity problem than anything. Because PHP only allows you to return one variable, you would need to introduce extra annotations logic at the PHPUnit level to tell it that what you are returning is not one value but an array of values.
On a side note I have found that returning objects with #depends can get tricky. If for example you had two tests that both depends on sets say #depends createUser. The result is a reference. Then testOne changes the user object it is given because it's a reference testTwo will now get a user object that has been changed.
I originally thought the idea of #depends returning values was awesome, but in practice I've stopped using it.

Weird Error "Call to a member function move() on a non-object" In Symfony2

I'm adding a file upload function to my form in Symfony2. I've followed the documentation here, but I keep getting the following error:
Fatal error: Call to a member function move() on a non-object
The thing is, the line of code it refers to is this:
$this->file->move($this->getUploadRootDir(), $this->file->getClientOriginalName());
This is a line of code that I took from the documentation. I'm not entirely sure why it's moaning about the move() though. I have checked to see if I'm missing any files but I'm not.
Do I have to create a reference to this? Or am I missing a file?
Cheers
EDIT:
I have added the following code to the beginning of the upload() function:
// the file property can be empty if the field is not required
if (null === $this->file) {
return;
}
However, I have now been given the following errors:
1/2: Exception: Serialization of
'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
and
2/2: Exception:
Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector::serialize()
must return a string or NULL
I don't know if what I've done has fixed the previous error as I have now been presented with these errors.
You cannot persit the file property, you need 2 properties in your entity, one to hold the UploadedFile and another one to hold the filename (which is persisted).
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
You add only the $file property to your form.
Add function
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload() {
if (null !== $this->file) {
$this->path = $this->file->getClientOriginalName();
}
}
then in upload function
public function upload() {
if (null === $this->file) {
return;
}
if ($this->file->move($this->getUploadRootDir(), $this->path)) {
// ok uploaded
}else{
echo "failed to upload";
}
}
You probably have
$this->file == null
Check that you have instanced it

Resources