Symfony 4, find() an entity - php

Thanks to the symfony doc https://symfony.com/doc/current/doctrine.html#updating-an-object, i am able to use get/set methods from my entity called 'Produit'.
But when i call the method setProduit() (from Paniers entity) Phpstorm say "expected App\Entity\Paniers, got Object" for the $produitSelected.
i don't know why phpstorm say that because i'm able to use methods, what's the problem ?
find() return an entity object, right ?
class PaniersController extends AbstractController
{
/**
* #Route("/paniers/add/{id}", name="paniers.add")
*/
public function addPanier(Request $request, Environment $twig, RegistryInterface $doctrine, $id)
{
$produitSelected = $doctrine->getRepository(Produit::class)->find($id);
if (!$produitSelected) {
throw $this->createNotFoundException(
'Pas de produit trouvé pour l\' id : '.$id
);
}
$lignePanier=$doctrine->getRepository(Paniers::class)->findOneBy(['produit' => $produitSelected, 'user' =>$this->getUser()]);
if($produitSelected->getStock()>0){
if ($lignePanier){
$quantite =$lignePanier->getQuantite();
$lignePanier->setQuantite($quantite+1);
} else {
$lignePanier = new Paniers();
$lignePanier->setUser($this->getUser())
->setDateAjoutPanier(new \DateTime(date('Y-m-d')))
->setQuantite(1)
->setProduit($produitSelected);
}
$doctrine->getManager()->persist($lignePanier);
$produitSelected->setStock($produitSelected->getStock()-1);
$doctrine->getManager()->persist($produitSelected);
$doctrine->getManager()->flush();
}
}
}
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\PaniersRepository")
* #ORM\Table(name="paniers")
*/
class Paniers
{
//...
/**
* #ORM\ManyToOne(targetEntity="Produit")
* #ORM\JoinColumn(name="produit_id", referencedColumnName="id")
*/
private $produit;
public function getProduit(): ?Produit
{
return $this->produit;
}
public function setProduit(?Produit $produit): self
{
$this->produit = $produit;
return $this;
}
}

In addition to Denis V's correct answer, I want to add that you could also modify your controller like this:
public function addPanier(Request $request, Environment $twig, RegistryInterface $doctrine,ProduitRepository $produitRepository, $id)
{
$produitSelected = $produitRepostiory->find($id);
//rest of the code
}
This way phpstorm also knows the type of the returned object, as every returned object is type hinted in the corresponding repository.

PhpStorm is apparently not that smart to understand that the actual type of the return value of find($id) in this case will be Produit. But you can help it:
/** #var Produit $produitSelected */
$produitSelected = $doctrine->getRepository(Produit::class)->find($id);
To make it working you should either use Produit with the full namespace or add the namespace directly within the type hint.
Of course this doesn't guarantee or ensure that the actual type will be Produit. So, if you make a mistake, PhpStorm will report the type incorrectly.

You first need to fix code :
Don't inject $id, you only need to typehint your entity: https://symfony.com/doc/current/best_practices/controllers.html#using-the-paramconverter
Don't inject $doctrine, use $em = $this->getDoctrine()->getManager();
Don't inject $twig, use return $this->render('...template', []);
Use English, it is always the rule. Not only other people could help you but also because Symfony understands it and you will need that when you start using form collections: https://symfony.com/doc/current/form/form_collections.html
Inject repository and you will have autocomplete and spot errors easier. Use ```make:entity`` command and you will see what I think, hard to explain.

Related

PhpStorm: Is there a way to enforce a type on the return statement using an inline PHPDoc annotation?

Consider the following code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
public static function getTheFirstCar(string $color): ?self
{
/** #var ?self */ // <-- Doesn't apply! Is there any alternative?
return (new self())->newQuery()->firstWhere('color', '=', $color);
}
}
The code is working correctly; nevertheless PhpStorm complains:
Return value is expected to be 'Car|null',
'\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model' returned
Assigning the result of the expression into an annotated variable resolves the warning, but yet introduces a "redundant" variable!
/** #var ?self $redundant */
$redundant = (new self())->newQuery()->firstWhere('color', '=', $color);
return $redundant;
So, is there a way in PhpStorm to enforce an inline type-annotation for the value of the return statement expression explicitly as Car|null, without introducing a redundant variable or specifying all of the expected return types?
You can suppress this warning by adding the #noinspection PhpIncompatibleReturnTypeInspection annotation before your statement.
I personally would not do this, but it's the only answer to you your question about how to "enforce" the return type and suppress warning afaik.
/** #noinspection PhpIncompatibleReturnTypeInspection */
return (new self())->newQuery()->where('color', '=', $color)->first();
If you decide to respect the warning, then this is probably the reason and solution for it:
newQuery() will create a new query on the models table (most likely: cars) without setting the appropriate model (Car).
Internally you're now running a bare query on cars. Therefore you will receive the appropriate record but is not gonna be an instance of Car, but an instance of Model instead. Therefore PhpStorm is expecting multiple additional return types here and printing this warning on your statement since it differs from the methods return type ?self.
The quick solution is to change newQuery() into newModelQuery(). This will create a new query and set the Model Car on the created query and return the appropriate instance or null
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Car extends Model
{
public static function getTheFirstCar(string $color): ?self
{
return (new self())->newModelQuery()->firstWhere('color', '=', $color);
// I'd use this statement though:
// return self::where('color', '=', $color)->first();
}
}
You need to add doc block to your class:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class Car
* #package App\Models
*
* #method self|Builder newQuery()
* #method ?self firstWhere($column, $operator = null, $value = null, $boolean = 'and')
*/
class Car extends Model
{
public static function getTheFirstCar(string $color): ?self
{
return (new self())->newQuery()->firstWhere('color', '=', $color);
}
}
As of PhpStorm 2022.2 anonymous #var annotations are supported, so your code already should work as is:
/** #var self */
return (new self())->newQuery()->firstWhere('color', '=', $color);

Symfony3 repository method doesn't work

I'm trying to call a method in the repository ArticleRepository from my controller ArticleController. However it says :
Undefined method 'afficheArticle'. The method name must start with either findBy or findOneBy!
my Entity Article : (Entity\Article.php)
/**
* Article
*
* #ORM\Table(name="Article", indexes={#ORM\Index(name="I_FK_Article_TypeArticle", columns={"idTypeArticle"})})
* #ORM\Entity(repositoryClass="erp-gkeep\new_erp\gkeepBundle\Repository\ArticleRepository")
*/
class Article
{
my ArticleController (Controller\ArticleController)
/**
* #Route("viewArticle2", name="viewArticle2")
*/
public function listAction2()
{
$data = $this->getDoctrine()->getRepository('gkeepBundle:Article')->afficheArticle();
my ArticleRepository
<?php
/**
* Created by PhpStorm.
*/
namespace gkeepBundle\Repository;
use Doctrine\ORM\EntityRepository;
class ArticleRepository extends EntityRepository
{
public function afficheArticle(){
$em=$this->getEntityManager();
$query = $em->createQuery(
'SELECT a.reference, a.designationfr, a.designationen, a.plan, a.url, a.datecreation, a.idtypearticle
FROM gkeepBundle:Article a
'
);
$articles = $query->getArrayResult();
return $articles;
}
}
if someone can help me please ! I'm pretty sure it's a stupid error :/
The value of repositoryClass in your mapping annotation needs to be the namespace + class name of your repository, it looks like you've maybe added some of the directory structure as well. - actually isn't a valid character in a PHP namespace, so the value you've got now definitely isn't right.
Try
#ORM\Entity(repositoryClass="gkeepBundle\Repository\ArticleRepository")
For reference: when this classname is not valid, Doctrine falls back to the default repository - this is what's throwing the error you're seeing.

PHPUnit Laravel Testing Controller that uses a Repository

I have read so many examples and cannot see what I am doing wrong, please if someone could help.
I am getting an error when running tests (error at the bottom of post), that doens't happen when viewing the page in the browser. I think this is because the repository isn't being instantiated properly so the relevant method not fired? Or some issue with the API call in the mock.
Controller:
namespace ShopApp\Http\Controllers\StoreFront;
use Illuminate\Http\Request;
use ShopApp\Http\Requests;
use ShopApp\Http\Controllers\Controller;
use ShopApp\Repositories\Contracts\CategoryRepositoryContract;
use ShopApp\Repositories\Contracts\PublicationRepositoryContract;
class PagesController extends Controller
{
private $publication;
private $category;
public function __construct(PublicationRepositoryContract $publication, CategoryRepositoryContract $category){
$this->publication = $publication;
$this->category = $category;
}
/**
* Homepage.
* #return view
* #internal param PublicationRepositoryContract $publication
* #internal param CategoryRepositoryContract $category
*/
public function home()
{
$mostRecent = $this->publication->getRecent();
return view('pages/home')->with(compact('mostRecent'));
}
}
Publication Repository:
<?php
namespace ShopApp\Repositories;
use ShopApp\Models\API\APIModel;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Support\Facades\Config;
use ShopApp\Repositories\Contracts\PublicationRepositoryContract;
class localPublicationRepository extends APIModel implements PublicationRepositoryContract
{
private $end_point; // where are we talking to?
public $response; //what did we get back?
public function __construct(GuzzleClient $client){
parent::__construct(new $client(['base_uri' => Config::get('customerprovider.local.api.base_uri'), 'http_errors' => true]));
$this->end_point = 'Publications';
}
/**
* Get all publications
*/
public function getAll(){
$this->response = $this->get($this->end_point);
$publications_with_slugs = $this->assembleSlugs($this->response);
return $publications_with_slugs;
}
/**
* Get recent publications
*/
public function getRecent(){
return $this->getAll(); //#todo - update this to just get the most recent
}
}
Test:
<?php
namespace Tests\Unit\Controllers;
use Tests\TestCase;
use Mockery as m;
class PagesControllerTest extends TestCase
{
public $publicationRepositoryContract;
/**
* Setup mocks etc
*/
public function setUp()
{
parent::setup();
$this->publicationRepositoryContract = m::mock('ShopApp\Repositories\Contracts\PublicationRepositoryContract');
}
/**
* Teardown mocks
*/
public function tearDown()
{
m::close();
parent::tearDown();
}
/**
* A basic test example.
*
* #return void
*/
public function testHomepage()
{
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once();
$this->app->instance('ShopApp\Repositories\Contracts\PublicationRepositoryContract', $this->publicationRepositoryContract);
$response = $this->call('GET', '/');
$response->assertStatus(200);
// getData() returns all vars attached to the response.
$mostRecent = $response->original->getData()['mostRecent'];
$response->assertViewHas('mostRecent');
$this->assertInstanceOf('Array', $mostRecent);
}
}
Test Error:
Expected status code 200 but received 500.
Failed asserting that false is true.
/home/vagrant/Code/imsnews-site/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:61
/home/vagrant/Code/imsnews-site/tests/Unit/Controllers/PagesControllerTest.php:53
Contents of Response ($response->Content()):
<span class="exception_title"><abbr title="ErrorException">ErrorException</abbr> in <a title="/home/vagrant/Code/imsnews-site/storage/framework/views/229655ca372490c9c0b1f5e7e2d4e91e6d3bbf6c.php line 262">229655ca372490c9c0b1f5e7e2d4e91e6d3bbf6c.php line 262</a>:</span>\n
<span class="exception_message">Invalid argument supplied for foreach() (View: /home/vagrant/Code/imsnews-site/resources/views/pages/home.blade.php)</span>\n
Line 262 from home.blade.php:
#foreach ($mostRecent as $key => $publication)
It seems clear that the method ->getRecent(), which in turn, calls ->getAll() on the publications repository is not returning an array as it should, but I don't know why.
Blade isn't complaining about the variable mostRecent not existing, it's complaining about it being invalid in a foreach.
Could this have something to do with Guzzle and the fact it's calling my API from the mocked test object?
Please help, hours have been lost..
Thanks.
Try mocking the concrete repository, and swap it out for the contract in the container. It seems you are mocking the contract, and then swapping it out for the same contract in your container.
TL;DR :
The key was you HAVE to have ->andReturn([]); on the test, like so:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once()->andReturn([]);
My test only had:
$this->publicationRepositoryContract
->shouldReceive('getRecent')
->once();
Thanks to Ayo for pointing this out. It only became clear after deleting other parts of my test.

Class XXX is not a valid document or mapped super class

I'm using doctrine Doctrine MongoDB ODM 1.0.3. When trying to update document using doctrine I'm getting the following error:
Class XXX is not a valid document or mapped super class.
I have the following class for the document:
<?php
namespace Documents;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* #ODM\Document(collection="posts")
*/
class Posts
{
/** #ODM\Id */
private $id;
/** #ODM\Field(type="string") */
private $title;
/** #ODM\EmbedMany(targetDocument="Comment") */
private $comments = array();
public function setTitle($title)
{
$this->title = $title;
}
public function getTitle()
{
return $this->title;
}
public function addComment($comment)
{
$this->comments[] = $comment;
}
public function getComments()
{
return $this->comments;
}
}
The following code is used to add new document:
$post = new \Documents\Posts();
$post->setTitle( $_POST['title'] );
$dm->persist($post);
$dm->flush();
Later I want to update the added document to add new comment for example. I use the following code:
$comment = new \Documents\Comment($_POST['comment_text']);
$dm->createQueryBuilder('Posts')
->update()
->field('comments')->push($comment)
->field('_id')->equals(new \MongoId($_POST['id']))
->getQuery()
->execute();
but getting the above mentioned error.
As you stated in your own answer you need to provide fully qualified name of class. Just wanted to add that better than to pass the string it is to use static class property like this instead: createQueryBuilder(\Documents\Posts::class); It works much better with IDEs (autocompletion, refactoring etc...)
For people getting this error in PHP 8 and above, check that you are not mixing annotations (e.g. /** #Entity */) and attributes (e.g. #[Entity]), and that you have indicated which method you are using in your config:
mappings:
App:
# pick one:
type: annotation
type: attribute
This always trips me up when I start a new project and the config defaults to annotations when I'm used to using attributes.
In case anyone else have a similar problem, you need to pass fully qualified class name to createQueryBuilder. My document classes are all inside Documents namespace so after passing it like this createQueryBuilder('\Documents\Posts') the problem is solved.
Based on the other varying answers here, it seems this error isn't super precise.
In my case the class was missing the EmbeddedDocument annotation.
namespace Foo;
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* #ODM\EmbeddedDocument
*/
class Bar { }

Symfony2 : Repository Class not found

Today I stuck in Repository Class Function I got this error
Undefined method 'test'. The method name must start with either findBy or findOneBy!
I allready checked these solutions -
Solution 1
Solution 2
Solution 3
Is anything I need to add into config file ?
This is my Entity Class
// src/Foo/NewsBundle/Entity/News.php
namespace Foo\NewsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* News
* #ORM\Entity(repositoryClass="Foo\NewsBundle\Repository\NewsRepository")
* #ORM\Table(name="news")
* #ORM\HasLifecycleCallbacks()
*/
class News
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $title;
This is my repository Class
// Foo/NewsBundle/Repository/NewsRepository.php
namespace Foo\NewsBundle\Repository;
use Doctrine\ORM\EntityRepository;
Class NewsRepository extends EntityRepository
{
public function test()
{
return "Nisarg";
}
}
And I am calling this test() function this wat from the controller
public function indexAction()
{
// $news = $this->getDoctrine()
// ->getRepository('FooNewsBundle:News')
// ->findAll();
$em = $this->getDoctrine()
->getManager();
$news = $em->getRepository('FooNewsBundle:News')->test();
if (!$news) {
throw $this->createNotFoundException('No news found');
}
$build['news'] = $news;
return $this->render('FooNewsBundle:Default:news_show_all.html.twig', $build);
}
Check if you have specified your repository class in your News orm config file.
There must be somthing like "repositoryClass: Foo\NewsBundle\Repository\NewsRepository"
And don't forget to clear cache!
In your entity you are not using annotation, check if you have a news.yml file in Resources/config/doctrine
I think the standard for repository classes is to put it in a subdirectory of the entity folder and still use the same entity namespace. Yours used a different namespace which is why I think you have the error.
According to the cookbook this is how the entity and custom respistory are defined.
link to custom repository class in the cookbook.
Entity
// src/Acme/StoreBundle/Entity/Product.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Acme\StoreBundle\Entity\ProductRepository")
*/
class Product
{
//...
}
Repository:
// src/Acme/StoreBundle/Entity/ProductRepository.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\EntityRepository;
class ProductRepository extends EntityRepository
{
public function findAllOrderedByName()
{
return $this->getEntityManager()
->createQuery(
'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC'
)
->getResult();
}
}
Fedor Petrick is right!
You should look for the orm file that corresponds to the entity.
In my case, I have created a custom repository named: OfertaRepository.php in the folder OfertaBundle\Entity
On the other hand, I have a file Oferta.orm.xml
In line three , It said :
<entity name="OfertaBundle\Entity\Oferta" table="oferta">
But it should be :
<entity name="OfertaBundle\Entity\Oferta" table="oferta" repository-class="OfertaBundle\Entity\OfertaRepository">
Now, the method in the OfertaRepository.php works well!
Your code look correct in the Entity and Repository. Perhaps you could try to call the getRepository directly without ->getManager.
$this->getDoctrine->getRepository('FooNewsBundle:News')->test();
If you need a specific field you should have a look at the short notations with findOneBy and findBy in most cases its much easier instead of writing a custom class.
http://symfony.com/doc/current/book/doctrine.html
Are you in production ? Perhaps clearing your cache is the solution :
php app/console cache:clear --env=prod --no-debug
If clearing your cache doesn't work, is $em->getRepository('FooNewsBundle:News') instanceOf Foo\NewsBundle\Repository\NewsRepository true or false? By the looks of things, your not getting the correct repository somehow?
have you generated the entities?
php app/console doctrine:generate:entities BUNDLENAME
launch this command and then retry your code

Categories