I'm new in Symfony2. I need to extend entity, also I need add custom methods to repository class.
What is best way to do this?
I need have possibility to access from this class entity class methods for example getCompanyId();
namespace Catalog\WebBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class ComCompany
{
private $cmpId;
public function getcmpId() {
return $this->cmpId
}
}
class ComCompanyService extends ComCompany
{
public function getcmpIdChanget() {
return ($this->cmpId) * 2
}
}
Also I need have possibility to access repository class methods
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('WebBundle:ComCompanyService')
->getcmpId();
Maybe it's poor example, but I thing you understand what I want. What I need to use for this ? Services or EntityRepository ? or something else ?
Related
As my IDE points out, the AbstractController::getDoctrine() method is now deprecated.
I haven't found any reference for this deprecation neither in the official documentation nor in the Github changelog.
What is the new alternative or workaround for this shortcut?
As mentioned here:
Instead of using those shortcuts, inject the related services in the constructor or the controller methods.
You need to use dependency injection.
For a given controller, simply inject ManagerRegistry on the controller's constructor.
use Doctrine\Persistence\ManagerRegistry;
class SomeController {
public function __construct(private ManagerRegistry $doctrine) {}
public function someAction(Request $request) {
// access Doctrine
$this->doctrine;
}
}
You can use EntityManagerInterface $entityManager:
public function delete(Request $request, Test $test, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$test->getId(), $request->request->get('_token'))) {
$entityManager->remove($test);
$entityManager->flush();
}
return $this->redirectToRoute('test_index', [], Response::HTTP_SEE_OTHER);
}
As per the answer of #yivi and as mentionned in the documentation, you can also follow the example below by injecting Doctrine\Persistence\ManagerRegistry directly in the method you want:
// src/Controller/ProductController.php
namespace App\Controller;
// ...
use App\Entity\Product;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Response;
class ProductController extends AbstractController
{
/**
* #Route("/product", name="create_product")
*/
public function createProduct(ManagerRegistry $doctrine): Response
{
$entityManager = $doctrine->getManager();
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(1999);
$product->setDescription('Ergonomic and stylish!');
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$entityManager->persist($product);
// actually executes the queries (i.e. the INSERT query)
$entityManager->flush();
return new Response('Saved new product with id '.$product->getId());
}
}
Add code in controller, and not change logic the controller
<?php
//...
use Doctrine\Persistence\ManagerRegistry;
//...
class AlsoController extends AbstractController
{
public static function getSubscribedServices(): array
{
return array_merge(parent::getSubscribedServices(), [
'doctrine' => '?'.ManagerRegistry::class,
]);
}
protected function getDoctrine(): ManagerRegistry
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".');
}
return $this->container->get('doctrine');
}
...
}
read more https://symfony.com/doc/current/service_container/service_subscribers_locators.html#including-services
In my case, relying on constructor- or method-based autowiring is not flexible enough.
I have a trait used by a number of Controllers that define their own autowiring. The trait provides a method that fetches some numbers from the database. I didn't want to tightly couple the trait's functionality with the controller's autowiring setup.
I created yet another trait that I can include anywhere I need to get access to Doctrine. The bonus part? It's still a legit autowiring approach:
<?php
namespace App\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Symfony\Contracts\Service\Attribute\Required;
trait EntityManagerTrait
{
protected readonly ManagerRegistry $managerRegistry;
#[Required]
public function setManagerRegistry(ManagerRegistry $managerRegistry): void
{
// #phpstan-ignore-next-line PHPStan complains that the readonly property is assigned outside of the constructor.
$this->managerRegistry = $managerRegistry;
}
protected function getDoctrine(?string $name = null, ?string $forClass = null): ObjectManager
{
if ($forClass) {
return $this->managerRegistry->getManagerForClass($forClass);
}
return $this->managerRegistry->getManager($name);
}
}
and then
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\Foobar;
class SomeController extends AbstractController
{
use EntityManagerTrait
public function someAction()
{
$result = $this->getDoctrine()->getRepository(Foobar::class)->doSomething();
// ...
}
}
If you have multiple managers like I do, you can use the getDoctrine() arguments to fetch the right one too.
On my Symfony 3.0 project I have a Entity named ImageGroups and a repository I made ImageGroupsRepository that Use ImageGroups entity. I also made an Images entity with a ImagesRepository
In ImageGroupsRepository I have a method named getUserImageGroups and on ImagesRepository I have a method named add.
What I want to ask is How to use the getUserImageGroups method from ImageGroupsRepository into add from ImagesRepository?
The answer given by A.L is correct, I just wanted to offer an alternative method to access the entity manager without having to call the function again, via $this->_em:
class ImagesRepository extends EntityRepository
{
public function add()
{
$imagesGroups = $this->_em
->getRepository('AcmeBundle:ImageGroups')
->getUserImageGroups();
// …
}
}
If you look at the documentation for EntityRepository you'll see that the getEntityManager() function simply returns the protected $_em member of the EntityRepository class.
In your repository, you can get the entity manager with $this->getEntityManager(), this allow to call getRepository() in order to get another repository:
class ImagesRepository extends EntityRepository
{
public function add()
{
$em = $this->getEntityManager();
$imagesGroups = $em
->getRepository('AcmeBundle:ImageGroups')
->getUserImageGroups();
// …
}
}
I want to replace the Laravels builder class with my own that's extending from it. I thought it would be as simple as matter of App::bind but it seems that does not work. Where should I place the binding and what is the proper way to do that in Laravel?
This is what I have tried:
my Builder:
use Illuminate\Database\Eloquent\Builder as BaseBuilder;
class Builder extends BaseBuilder
{
/**
* Find a model by its primary key.
*
* #param mixed $id
* #param array $columns
* #return \Illuminate\Database\Eloquent\Model|static|null
*/
public function find($id, $columns = array('*'))
{
Event::fire('before.find', array($this));
$result = parent::find($id, $columns);
Event::fire('after.find', array($this));
return $result;
}
}
And next I tried to register the binding in bootstrap/start.php file like this :
$app->bind('Illuminate\\Database\\Eloquent\\Builder', 'MyNameSpace\\Database\\Eloquent\\Builder');
return $app;
Illuminate\Database\Eloquent\Builder class is an internal class and as such it is not dependency injected into the Illuminate\Database\Eloquent\Model class, but kind of hard coded there.
To do what you want to do, I would extend the Illuminate\Database\Eloquent\Model to MyNamespace\Database\Eloquent\Model class and override newEloquentBuilder function.
public function newEloquentBuilder($query)
{
return new MyNamespace\Database\Eloquent\Builder($query);
}
Then alias MyNamespace\Database\Eloquent\Model to Eloquent at the aliases in app/config/app.php
Both of the answers are correct in some way. You have to decide what your goal is.
Change Eloquent Builder
For example, if you want to add a new method only for eloquent models (eg. something like scopes, but maybe a little more advanced so it’s not possible in a scope)
Create a new Class extending the Eloquent Builder, for Example CustomEloquentBuilder.
use Illuminate\Database\Eloquent\Builder;
class CustomEloquentBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newEloquentBuilder
use Namespace\Of\CustomEloquentBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
public function newEloquentBuilder($query)
{
return new CustomEloquentBuilder($query);
}
}
Change Database Query Builder
For example to modify the where-clause for all database accesses
Create a new Class extending the Database Builder, for Example CustomQueryBuilder.
use Illuminate\Database\Query\Builder;
class CustomQueryBuilder extends Builder
{
public function myMethod()
{
// some method things
}
}
Create a Custom Model and overwrite the method newBaseQueryBuilder
use Namespace\Of\CustomQueryBuilder;
use Illuminate\Database\Eloquent\Model;
class CustomModel extends Model
{
protected function newBaseQueryBuilder()
{
$connection = $this->getConnection();
return new CustomQueryBuilder(
$connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
);
}
}
Laravel Version: 5.5 / this code is untestet
The answer above doesn't exactly work for laravel > 5 so I done some digging and I found this!
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Database/Eloquent/Model.php#L1868
use this instead!
protected function newBaseQueryBuilder()
{
$conn = $this->getConnection();
$grammar = $conn->getQueryGrammar();
return new QueryBuilder($conn, $grammar, $conn->getPostProcessor());
}
I'm using the Sentinel bundle for Laravel.
The vendor class UserController has a constructor that injects a bunch of dependencies into it that looks like this:
<?php namespace Sentinel;
use Sentinel\Repo\User\UserInterface;
use Sentinel\Repo\Group\GroupInterface;
use Sentinel\Service\Form\Register\RegisterForm;
use Sentinel\Service\Form\User\UserForm;
use Sentinel\Service\Form\ResendActivation\ResendActivationForm;
use Sentinel\Service\Form\ForgotPassword\ForgotPasswordForm;
use Sentinel\Service\Form\ChangePassword\ChangePasswordForm;
use Sentinel\Service\Form\SuspendUser\SuspendUserForm;
use BaseController, View, Input, Event, Redirect, Session, Config;
class UserController extends BaseController {
protected $user;
protected $group;
protected $registerForm;
protected $userForm;
protected $resendActivationForm;
protected $forgotPasswordForm;
protected $changePasswordForm;
protected $suspendUserForm;
/**
* Instantiate a new UserController
*/
public function __construct(
UserInterface $user,
GroupInterface $group,
RegisterForm $registerForm,
UserForm $userForm,
ResendActivationForm $resendActivationForm,
ForgotPasswordForm $forgotPasswordForm,
ChangePasswordForm $changePasswordForm,
SuspendUserForm $suspendUserForm)
{
$this->user = $user;
$this->group = $group;
$this->registerForm = $registerForm;
$this->userForm = $userForm;
$this->resendActivationForm = $resendActivationForm;
$this->forgotPasswordForm = $forgotPasswordForm;
$this->changePasswordForm = $changePasswordForm;
$this->suspendUserForm = $suspendUserForm;
//Check CSRF token on POST
$this->beforeFilter('Sentinel\csrf', array('on' => array('post', 'put', 'delete')));
// Set up Auth Filters
$this->beforeFilter('Sentinel\auth', array('only' => array('change')));
$this->beforeFilter('Sentinel\inGroup:Admins', array('only' => array('show', 'index', 'create', 'destroy', 'suspend', 'unsuspend', 'ban', 'unban', 'edit', 'update')));
//array('except' => array('create', 'store', 'activate', 'resend', 'forgot', 'reset')));
}
// The rest of the class code...
I've created model class in my app called ExtendedUserController so that I can add functionality to the existing controller. My controller class looks like this currently:
<?php
use Sentinel\UserController;
use Sentinel\Repo\Group\SentryGroup;
class ExtendedUserController extends UserController {
// The rest of the class code...
I don't have a constructor in my class because I'm letting the parent class construct when I extend it.
I have a new model that I would like to add to my controller class. My plan was to inject the dependency in a constructor for the ExtendedUserController and then call `parent::__construct()' to allow the parent class to be extended.
I'm running into errors when loading the class and I'm still green enough to laravel and dependency injection that I'm not exactly sure how to fix it or if this is even the proper way of accomplishing the task:
<?php
use Sentinel\UserController;
use Sentinel\Repo\Group\SentryGroup;
use CustomerLinks;
class ExtendedUserController extends UserController {
protected $customerLinks;
public function __construct(CustomerLinks $customerLinksClass){
$this->customerLinks = $customerLinksClass;
parent::__construct();
}
I'm running into the error:
Argument 1 passed to Sentinel\UserController::__construct() must be an
instance of Sentinel\Repo\User\UserInterface, none given, called in
/var/www/app/controllers/ExtendedUserController.php on line 15 and
defined
If I run the parent constructor, shouldn't the parent's code automatically pass in it's dependencies as outlined in the parent class' code?
Is this not the proper way to add the dependency? Any help and explanation would be greatly appreciated.
I realize the error now and I wanted to add it in rather than deleting the question.
I found an earlier stackoverflow post on this issue when searching with different criteria.
I needed to
Add the parent class' dependencies to my extended class, pass them into my extended classes
Inject the dependencies into my extended class' constructor
Pass those injected variables into the parent::__construct() call
Here's what the working code looks like:
<?php
use Sentinel\UserController;
use Sentinel\Repo\Group\SentryGroup;
use CustomerLinks;
// Parent class' dependencies
use Sentinel\Repo\User\UserInterface;
use Sentinel\Repo\Group\GroupInterface;
use Sentinel\Service\Form\Register\RegisterForm;
use Sentinel\Service\Form\User\UserForm;
use Sentinel\Service\Form\ResendActivation\ResendActivationForm;
use Sentinel\Service\Form\ForgotPassword\ForgotPasswordForm;
use Sentinel\Service\Form\ChangePassword\ChangePasswordForm;
use Sentinel\Service\Form\SuspendUser\SuspendUserForm;
use BaseController, View, Input, Event, Redirect, Session, Config;
class ExtendedUserController extends UserController {
protected $customerLinks;
// Injecting both the parent class' dependencies and my new dependency into the constructor
public function __construct(CustomerLinks $customerLinksClass,UserInterface $user, GroupInterface $group, RegisterForm $registerForm, UserForm $userForm,ResendActivationForm $resendActivationForm,ForgotPasswordForm $forgotPasswordForm,ChangePasswordForm $changePasswordForm,
SuspendUserForm $suspendUserForm){
// passing the injected variables to the parent constructor call
parent::__construct($user, $group, $registerForm, $userForm,$resendActivationForm,$forgotPasswordForm,$changePasswordForm,$suspendUserForm);
// Adding my new injected class into the controller class
$this->customerLinks = $customerLinksClass;
}
//The rest of the class code...
It was a pretty big face-palm moment. Hopefully this will help someone out in the future.
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