How to load an Entity from another Bundle on Symfony2 - php

Let's say I have two Bundles :
Compagny\InterfaceBundle
Compagny\UserBundle
How can I load an Entity of UserBundle in the controller of InterfaceBundle ?
The Controller of my Compagny/InterfaceBundle :
<?php
// src/Compagny/InterfaceBundle/Controller/DefaultController.php
namespace Compagny\InterfaceBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Compagny\UserBundle\Entity; // I believed this line will do the trick, but it doesn't
class DefaultController extends Controller
{
public function indexAction()
{
$user = new User();
}
}
The Entity of my Compagny/UserBundle :
<?php
namespace Compagny\UserBundle\Entity
class User {
public $name;
public function setName($name) {
// ...
}
public function getName() {
// ...
}
}
(Let's says for this example that the User class doesn't use Doctrine2, because it doesn't need to connect to the database).

<?php
// src/Compagny/InterfaceBundle/Controller/DefaultController.php
namespace Compagny\InterfaceBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Compagny\UserBundle\Entity\User; // It's not a trick, it's PHP 5.3 namespacing!
class DefaultController extends Controller
{
public function indexAction()
{
$user = new User();
}
}
You can of course, you are just using a class from another namespace. The fact that it is an entity is not important at all! You can of course query the entity manager for that entity as well.

Related

getSource() returns an empty table in CakePHP 4

I am currently updating a project from CakePHP 3 to CakePHP 4, and in this project I have a Trait that implements a "common" virtual property that some of the entities need.
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class NewsTable extends Table
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->setTable('news');
$this->setPrimaryKey('id');
// more config here...
}
}
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class News extends Entity
{
use SitemapTrait;
protected $_virtual = ['sitemap']; // this is the virtual property that the trait should take care of
}
<?php
namespace App\Model\Entity;
use Cake\ORM\TableRegistry;
trait SitemapTrait
{
protected function _getSitemap()
{
$table = TableRegistry::getTableLocator()->get($this->getSource());
// more logic here...
}
}
The problem is that, on the _getSitemap() method, $this->getSource() returns a generic Cake\ORM\Table object, rather than a App\Model\Table\NewsTable object as I would expect, so everything that should happen afterward will not work.
I also wrote a simple command to check what happens when getSource() is called directly on an entity, and in this case I get the correct result, so the problem seems to be specifically related to it being called inside a Trait:
<?php
namespace App\Command;
use Cake\Command\Command;
use Cake\Console\ConsoleIo;
use Cake\Console\Arguments;
use Cake\ORM\TableRegistry;
class OrmTestCommand extends Command
{
protected $newsTable;
public function __construct()
{
$this->newsTable = TableRegistry::getTableLocator()->get('News');
}
public function execute(Arguments $args, ConsoleIo $io)
{
$news = $this->newsTable->find('all')
->first();
print_r($news->getSource());
}
}
In this case I correctly get a App\Model\Table\NewsTable object.
I don't get why this broke going from CakePHP 3.5.18 to 4.3.11. Do I need some extra config in my model? What am I doing missing or doing wrong?

Laravel external class files

I have a file that i put in app\Classes\myVendor\dev_client_api.php. This file has a class in it:
class someClass{
//stuff
}
I want to use this class in a controller.
In my controller I have done the following:
namespace App\Classes\myVendor;
use dev_client_api;
class myController extends Controller
{
///stuff
public function processData(Request $request){
$client = new someClass($vars);
}
}
When i execute this page I get:
Class 'App\Classes\myVendor\Controller' not found
I have to admit I am not sure what exactly I am doing. Any help would be great.
I assume your Controllers are in Laravel's default App\Http\Controller directory.
namespace App\Classes\myVendor;
class someClass {
//stuff
}
namespace App\Http\Controllers;
use App\Classes\myVendor\someClass;
class myController extends Controller
{
///stuff
public function processData(Request $request){
$client = new someClass($vars);
}
}

How to register a default Observer for every Model instance with Laravel

Laravel 5.1
I'm trying to register a single model observer for every Model that extends my AbstractModel (who are extending Illuminate\Database\Eloquent\Model).
The problem is my GenericModelObserver can't listen to events fired by Models inheriting AbstractModel.
Let me show what I did so far.
A Service Provider was created and put on the last position of the providers array inside config/app.php
<?php
// app/Providers/ObserverServiceProvider.php
namespace App\Providers;
use App\Models\Quotation;
use App\Models\AbstractModel;
use App\Observers\QuotationObserver;
use App\Observers\GenericModelObserver;
use Illuminate\Support\ServiceProvider;
class ObserverServiceProvider extends ServiceProvider
{
public function boot()
{
AbstractModel::observe(GenericModelObserver::class);
Quotation::observe(QuotationObserver::class);
}
public function register()
{
}
}
Then I have my plain simple GenericModelObserver
<?php
// app/Observers/GenericModelObserver.php
namespace App\Observers;
use App\Models\AbstractModel;
class GenericModelObserver
{
public function saving(AbstractModel $model)
{
return $model->valid();
}
}
The Abstract Model
<?php
// app/Models/AbstractModel.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AbstractModel extends Model
{
// ...
}
My Quotation Model
<?php
// app/Models/Quotation.php
namespace App\Models;
class Quotation extends AbstractModel
{
// ...
}
When Quotation is saved, the GenericModelObserver can't listen to the saving event or any other event.
The same applies for other Models that don't have a specific Model Observer.
Is this the right strategy? I would not like to bind a observer to every model through the boot method.
Instead of extending model - write your own trait which will work as observer.
Below I wrote some basic trait:
<?php
namespace App\YourPackage\Traits;
use Illuminate\Database\Eloquent\Model;
trait Observable
{
public static function bootObservable()
{
static::updating(function (Model $model) {
dd('updating');
});
}
}
and use it by typing use Observable; in your model class.
Also for your learning take a note how traits is booting: You have to put boot[TraitClassName] method into trait, to boot it properly.
Never write boot method inside your trait, it's dangerous!
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Observers\TeamObserver;
class Team extends Model
{
/**
* The "booting" method of the model.
*
* #return void
*/
protected static function boot()
{
parent::boot();
self::observe(TeamObserver::class);
}
}
Why not simply extend a parent class say BaseObserver
I have something similar in my caching system
<?php namespace App\Observers;
class BaseObserver {
public function saving($model)
{
//do your thing here that apply to all observers, like caching
}
}
Then in your Observers
<?php namespace App\Observers;
class Quotation extends BaseObserver{
//you can override any of the methods if you wish
}
Update the boot method in your AppServiceProvider to the following:
public function boot()
{
# Register all model observers
$filesInFolder = \File::files(app_path('/Observers'));
foreach($filesInFolder as $path) {
$observerClassName = pathinfo($path)['filename'];
$className = str_replace('Observer', '', $observerClassName);
$observerClassName = 'App\\Observers\\' . $observerClassName;
$className = 'App\\' . $className;
$className::observe($observerClassName);
}
}
Models should follow this format:
App\User
Observers should follow this format:
App\Observers\UserObserver
When models are in the 'models' folder:
Swap this $className = 'App\\' . $className; for this $className = 'App\\Models\\' . $className;
In your parent model you can do something like that
/**
* If true will attach the observers of the parent class
* #var bool
*/
protected $shouldAttachParentObservers = true;
public static function boot()
{
$instance = new static;
$instance->attachParentObservers();
parent::boot();
}
public function attachParentObservers() {
$parentClass = get_parent_class($this);
if(!empty($parentClass) && $this->shouldAttachParentObservers) {
$eventObservers = [];
foreach ($this->getObservableEvents() as $event) {
$eventObservers[$event] = ($this::$dispatcher->getListeners("eloquent.{$event}: {$parentClass}"));
foreach ($eventObservers[$event] as $observer) {
$eventName = "eloquent.{$event}: {$this::getClassName()}";
$this::$dispatcher->listen($eventName, $observer);
}
}
}
}
/**
* You may use different way to find the class name
*/
public static function getClassName() {
return static::class;
}

Symfony2 How to have access to a set of data in all controllers?

I have searched but I can't find any similar issues or maybe I am phrasing it wrong. What I want to achieve is access to an object in all the controllers in my Bundle. For example:
<?php
namespace Example\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class FolderController extends Controller
{
function indexAction()
{
$title = $this->folder->getTitle();
$description = $this->folder->getDescription();
}
}
Usually outside of Symfony I would have extended the Controller class myself with BaseController extends Controller and set this up in the construct method but I know Symfony doesn't use the construct method so I am a bit stuck as to where to go.
I would usually do something like this:
class BaseController extends Controller
{
function __construct()
{
parent::__construct();
//load up the folder from my model with an ID
$this->folder = $folder;
}
}
I would then extend BaseController from FolderController and go from there but I have tried this with Symfony and it does not work. I have looked into Services but do not think this is what I need to make this work. If any more details are required please let me know, thanks.
If I understand your question correctly, services are indeed what you're looking for.
First, define a service in services.yml:
services:
vendor.folder_manager:
class: Vendor\FolderBundle\Entity\Manager\FolderManager
arguments:
em: "#doctrine.orm.entity_manager"
class: Vendor\FolderBundle\Entity\Folder
Then create that FolderManager class:
<?php
namespace Vendor\FolderBundle\Entity\Manager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
class FolderManager
{
protected $em;
protected $repo;
protected $class;
public function __construct(EntityManager $em, $class) {
$this->em = $em;
$this->class = $class;
$this->repo = $em->getRepository($class);
}
public function findById($id) {
return $this->repo->findById($id);
}
public function getRepository() {
return $this->repo;
}
}
Finally, in your controller:
$this->container->get('vendor.folder_manager')->findById($folderId);
Or:
$this->container->get('vendor.folder_manager')->getRepository()->findById($folderId);
Symfony2 will automatically inject the entity manager the class into the manager, so all you have to provide in the controller is the folder's id.
Edit:
To make it prettier, you could also make a shortcut function in your controller:
protected function getFolderManager()
{
return $this->container->get('vendor.folder_manager');
}
Then you can do:
$this->getFolderManager()->findById($folderId);

Loading a Parent Class in Symfony 2

I've set up a test parent class in my Symfony 2 controller as follows:
<?php
namespace Zetcho\AmColAnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BaseController extends Controller
{
public function validateUser()
{
$user['first_name'] = "Name";
$user['signin'] = true;
return $user;
}
}
class DefaultController extends BaseController
{
public function indexAction()
{
$user = $this->validateUser();
$displayParms['user'] = $user;
return $this->render('ZetchoAmColAnBundle:Default:index.html.twig',$displayParms);
}
}
The code is in src/Zetcho/AmColAnBundle/Controller/DefaultController.php
The test code works. I'd now like to move the parent class (BaseController) out of the controller file to its own so I can reuse it in my other controllers. I want to put it in the same directory as the other controllers and I'd like to declare it the same way as the Controller in the use statement above. What's the best/accepted way to do this in Symfony 2?
You do this in Symfony2 exactly the same way as you would with any PHP class. Split your classes into separate files like this:-
src/Zetcho/AmColAnBundle/Controller/BaseController.php
namespace Zetcho\AmColAnBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BaseController extends Controller
{
public function validateUser()
{
$user['first_name'] = "Name";
$user['signin'] = true;
return $user;
}
}
src/Zetcho/AmColAnBundle/Controller/DefaultController.php
namespace Zetcho\AmColAnBundle\Controller;
use Zetcho\AmColAnBundle\Controller\BaseController;
class DefaultController extends BaseController
{
public function indexAction()
{
$user = $this->validateUser();
$displayParms['user'] = $user;
return $this->render('ZetchoAmColAnBundle:Default:index.html.twig',$displayParms);
}
}
Its really quite simple once you know how. Remember that controllers in symfony2 are just normal PHP classes, there is nothing special about them.

Categories