I am trying to use DataTablesBundle in Symfony 4.2.
There is very good manual and starter however it is used with extends Controller class. But I am trying to use it with AbstractController
class.
So I have such issue:
Service "Omines\DataTablesBundle\DataTableFactory" not found: even
though it exists in the app's container, the container inside
"App\Controller\StaffController" is a smaller service locator that
only knows about the "doctrine", "form.factory", "http_kernel",
"parameter_bag", "request_stack", "router",
"security.authorization_checker", "security.csrf.token_manager",
"security.token_storage", "serializer", "session" and "twig" services.
Try using dependency injection instead
I have found a solution however I have just started with Symfony so I haven't really understood what it is about:
https://github.com/omines/datatables-bundle/commit/cd9b93eac9ef4fd3c1459305c71ca6e2ac0c444e
If using AbstractController instead, which is currently
recommended practice, ensure you subscribe to the
DataTableFactory service yourself. Alternatively you can
bypass the convenience trait and inject the service via regular
constructor injection
How to subscribe to the DataTableFactory service?
And what does the alternative "bypass the convenience trait and inject the service" mean?
I don't know how to subscribe to a service, but here is how to inject it.
Add a constructor to your controller and inject the DataTableFactory:
//use Omines\DataTablesBundle\DataTableFactory;
private $factory;
public function __construct(
DataTableFactory $factory
) {
$this->factory = $factory;
}
/**
* Creates and returns a basic DataTable instance.
*
* #param array $options Options to be passed
* #return DataTable
*/
protected function createDataTable(array $options = [])
{
return $this->factory->create($options);
}
/**
* Creates and returns a DataTable based upon a registered DataTableType or an FQCN.
*
* #param string $type FQCN or service name
* #param array $typeOptions Type-specific options to be considered
* #param array $options Options to be passed
* #return DataTable
*/
protected function createDataTableFromType($type, array $typeOptions = [], array $options = [])
{
return $this->factory->createFromType($type, $typeOptions, $options);
}
Then you can use it like this:
$this->createDateTable();
Related
I have a service file containing following lines of code.
namespace App\Service;
/**
* Class MyService
* #package App\Service
*/
class MyService
{
/**
* #var string
*/
private $newTest;
/**
* MyService constructor.
* #param string $newTest
*/
public function __construct(string $newTest)
{
$this->newTest = $newTest;
}
}
I have defined it under service section in services.yaml file.
my_service:
class: App\Service\MyService
bind:
$pcValue: '%env(resolve:LATEST)%'
Now I am trying to retrieve this pcValue at the controller,
$this->get('my_service');
It is returning me following error:
`Service "my_service" not found: even though it exists in the app's container, the container inside "App\Controller\WheelController" is a smaller service locator that only knows about the "doctrine", "form.factory", "http_kernel", "parameter_bag", "request_stack", "router", "security.authorization_checker", "security.csrf.token_manager", "security.token_storage", "serializer", "session" and "twig" services. Try using dependency injection instead.`
The problem is that you are not using dependency injection correctly to take advantage of auto-wiring.
In order to have your service available on your controller you just need to add the type-hinted service in the desired action in your controller.
Check this
In my app I have a service called "LogService" to log events and other items. I basically need to use this on every controller to log events by users. Instead of having to instantiate this service in each controller, I had two thoughts for accomplishing this.
Option 1: Bind the service into the IoC and then resolve it that way
Option 2: Make a master class with the service in it and then extend it for other classes so they come with the service already bound
I have questions for each of these methods:
Option 1: Is this even possible? If so, would it just be with "App::make()" that it would be called? That way doesn't seem to play too well with IDE's
Option 2: I have done this kind of thing in the past but PHPStorm does not seem to recognize the service from the parent object because it is instantiated by "App::make()" and not through the regular dependency injection.
What would be the best course of action?
Thanks!
You can have it both ways, I think the neatest way would be:
1) Have an interface that describes your class, let's call it LogServiceInterface
2) Create a Service Provider that instantiates your class, like so:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class LoggerServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind(LogServiceInterface::class, function($app)
{
return new LogService();
});
}
}
3) Register this service provider in config/app.ph file:
'providers' => [
// Other Service Providers
App\Providers\LoggerServiceProvider::class,
],
4) Now, in controller you can request the instance of something that implements LoggerServiceInterface straight in the constructor:
(Some controller):
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* The logger service.
* #var LoggerServiceInterface $loggerService
*/
protected $loggerService;
/**
* Create a controller instance.
*
* #param OrderRepository $orders
* #return void
*/
public function __construct(LoggerServiceInterface $loggerService)
{
$this->loggerService = $loggerService;
}
/**
* Show all of the orders.
*
* #return Response
*/
public function index()
{
// $this->loggerService will be an instance of your LoggerService class that
// is instantiated in your service provider
}
}
This way, you have got an easy way to quickly change the implementation of your service, moreover, Phpstorm can handle this very easily.
You will still be able to use app()->make() to obtain an instance of your service.
This, however, will not be automatically picked up by Phpstorm. But you can help it to understand that, all you need to do is to use #var annotation, see:
/**
* #var LoggerServiceInterface $logger
*/
$logger = app()->make(LoggerServiceInterface::class);
That way, Phpstorm will know what to expect from that $logger object.
In order to refactor the code about the ticket notification systems, I created a Doctrine listener:
final class TicketNotificationListener implements EventSubscriber
{
/**
* #var TicketMailer
*/
private $mailer;
/**
* #var TicketSlackSender
*/
private $slackSender;
/**
* #var NotificationManager
*/
private $notificationManager;
/**
* We must wait the flush to send closing notification in order to
* be sure to have the latest message of the ticket.
*
* #var Ticket[]|ArrayCollection
*/
private $closedTickets;
/**
* #param TicketMailer $mailer
* #param TicketSlackSender $slackSender
* #param NotificationManager $notificationManager
*/
public function __construct(TicketMailer $mailer, TicketSlackSender $slackSender, NotificationManager $notificationManager)
{
$this->mailer = $mailer;
$this->slackSender = $slackSender;
$this->notificationManager = $notificationManager;
$this->closedTickets = new ArrayCollection();
}
// Stuff...
}
The goal is to dispatch notifications when a Ticket or a TicketMessage entity is created or updated trough mail, Slack and internal notification, using Doctrine SQL.
I already had a circular dependencies issue with Doctrine, so I injected the entity manager from the event args instead:
class NotificationManager
{
/**
* Must be set instead of extending the EntityManagerDecorator class to avoid circular dependency.
*
* #var EntityManagerInterface
*/
private $entityManager;
/**
* #var NotificationRepository
*/
private $notificationRepository;
/**
* #var RouterInterface
*/
private $router;
/**
* #param RouterInterface $router
*/
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
/**
* #param EntityManagerInterface $entityManager
*/
public function setEntityManager(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
$this->notificationRepository = $this->entityManager->getRepository('AppBundle:Notification');
}
// Stuff...
}
The manager is injected form the TicketNotificationListener
public function postPersist(LifecycleEventArgs $args)
{
// Must be lazy set from here to avoid circular dependency.
$this->notificationManager->setEntityManager($args->getEntityManager());
$entity = $args->getEntity();
}
The web application is working, but when I try to run a command like doctrine:database:drop for example, I got this:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException]
Circular reference detected for service "doctrine.dbal.default_connection", path: "doctrine.dbal.default_connection -> mailer.ticket -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_provider.username_email -> fos_user.user_manager".
But this is concerning vendor services.
How to solve this one? Why I have this error only on cli?
Thanks.
Had the same architectural problem lately, assuming you use Doctrine 2.4+ the best thing to do is not use the EventSubscriber (which triggers for all events), but use EntityListeners on the two entities you mention.
Assuming that the behavior of both entities should be the same, you could even create one listener and configure it for both entities. The annotation looks like this:
/**
* #ORM\Entity()
* #ORM\EntityListeners({"AppBundle\Entity\TicketNotificationListener"})
*/
class TicketMessage
Thereafter you can create the TicketNotificationListener class and let a service definition do the rest:
app.entity.ticket_notification_listener:
class: AppBundle\Entity\TicketNotificationListener
calls:
- [ setDoctrine, ['#doctrine.orm.entity_manager'] ]
- [ setSlackSender, ['#app.your_slack_sender'] ]
tags:
- { name: doctrine.orm.entity_listener }
You might not even need the entity manager here, because the entity itself is available via the postPersist method directly:
/**
* #ORM\PostPersist()
*/
public function postPersist($entity, LifecycleEventArgs $event)
{
$this->slackSender->doSomething($entity);
}
More info on Doctrine entity listeners: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#entity-listeners
IMHO you are mixing 2 different concepts here:
Domain Events (TicketWasClosed for example)
Doctrine's Life-cycle Events (PostPersist for example)
Doctrine's event system is meant to hook into the persistence flow, to deal stuff directly related to saving to and loading from the database. It shouldn't be used for anything else.
To me it looks like what you want to happen is:
When a ticket was closed, send a notification.
This has nothing to do with Doctrine or persistence in general. What you need is another event system dedicated to Domain Events.
You can still use the EventManager from Doctrine, but make sure you create a second instance which you use for Domain Events.
You can also use something else. Symfony's EventDispatcher for example. If you're using the Symfony framework, the same thing applies here as well: don't use Symfony's instance, create your own for Domain Events.
Personally I like SimpleBus, which uses objects as events instead of a string (with an object as "arguments"). It also follows the Message Bus and Middleware patterns, which give a lot more options for customization.
PS: There are a lot of really good articles on Domain Events out there. Google is your friend :)
Example
Usually Domain Events are recorded within entities themselves, when performing an action on them. So the Ticket entity would have a method like:
public function close()
{
// insert logic to close ticket here
$this->record(new TicketWasClosed($this->id));
}
This ensures the entities remain fully responsible for their state and behavior, guarding their invariants.
Of course we need a way to get the recorded Domain Events out of the entity:
/** #return object[] */
public function recordedEvents()
{
// return recorded events
}
From here we probably want 2 things:
Collect these events into a single dispatcher/publisher.
Only dispatch/publish these events after a successful transaction.
With the Doctrine ORM you can subscribe a listener to Doctrine's OnFlush event, that will call recordedEvents() on all entities that are flushed (to collect the Domain Events), and PostFlush that can pass those to a dispatcher/publisher (only when successful).
SimpleBus provides a DoctrineORMBridge that supplies this functionality.
Is dependency injection of a custom class in a command possible?
I'm trying this:
<?php
namespace vendor\package\Commands;
use Illuminate\Console\Command;
use vendor\package\Models\Log;
use vendor\package\Updates\UpdateStatistics;
class UpdatePublishmentStats extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'vendorname:updatePublishmentStats';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Updates Twitter followers & Facebook page likes';
/**
* Contact implementation
* #var vendor\package\Update\UpdateStatistics
*/
protected $stats;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(
Log $log,
UpdateStatistics $stats
) {
parent::__construct();
$this->log = $log;
$this->stats = $stats;
}
But when I try to do this:
public function handle()
{
$this->stats->updateFbStats();
}
I suddenly get Segmentation fault: 11
When I delete the use vendor\package\Updates\UpdateStatistics; part, I don't get that error.
So what am I doing wrong here? Is it not possible to use dependency injection in a command?
You can inject any service in the handle method:
Note that we are able to inject any dependencies we need into the command's handle method.
Source: https://laravel.com/docs/5.8/artisan#command-structure
According to the Command Structure section of 5.2 documentation (https://laravel.com/docs/5.2/artisan#writing-commands):
"Note that we are able to inject any dependencies we need into the command's constructor. The Laravel service container will automatically inject all dependencies type-hinted in the constructor."
So I think you're good there, as far as the capability being present and available.
As for getting it to work, for me the segfault points to something wrong with the UpdateStats class, how it's referenced in the service container, or how its being resolved from the service container.
I don't have a definitive answer, but what I would do is try another class and see if I could localize the issue to this particular class, or if the problem happens with others, and then try and debug from there.
Also, if you just can't get that to work, the app() function will resolve items from the service container when you want (although looking through the 5.2 docs I don't see it anymore, so it may be deprecated - I do see $this->app->make() however).
This may work for you if nothing else does:
public function __construct(
Log $log,
) {
parent::__construct();
$this->log = $log;
$this->stats = app(UpdateStatistics::class);
}
My guess is, however, that you will get a segfault with this as well, as it should try resolving the same class the same way. If you do, then at least the error is a little clearer, and unrelated to auto-injecting feature.
Hope that at least helps a little.
Update on the app() function
So the app() function does not appear to be documented, but I have 5.2 installed right now and the helpers.php file in Illuminate/Foundation definitely has the function:
if (! function_exists('app')) {
/**
* Get the available container instance.
*
* #param string $make
* #param array $parameters
* #return mixed|\Illuminate\Foundation\Application
*/
function app($make = null, $parameters = [])
{
if (is_null($make)) {
return Container::getInstance();
}
return Container::getInstance()->make($make, $parameters);
}
}
Unfortunately the API documentation doesn't include any of the helper functions, but the current master, 5.2, and 5.3 versions of the file on Github all have the function:
https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Foundation/helpers.php#L91
https://github.com/laravel/framework/blob/5.2/src/Illuminate/Foundation/helpers.php#L91
Sorry for perhaps not the most accurate title. I'm having trouble figuring out what this would even be called.
I'm somewhat new to OOP with php, as most of my time is spent with procedural programming. In an effort to better learn these concepts, I'm taking an existing application and rewriting portions of it using OOP. The below example is how I set up the base class, and then I extended the base class into several smaller classes for easier maintainability. Below, you can see how I extended the base class to create a user class. Please note, that my class definitions are in separate files, but I have a working autoloader that automatically registers them
class EventScheduler{
function __construct(){
// set up database connections here
}
}
class User extends EventScheduler{
private function getUserProfile($username){
// return an array here representing
// details of passed username from database
}
public function getUserType($username){
return $this->getUserProfile($username)['user_type'];
}
}
What I'd like to be able to do is reference the User class from inside the base class, like this:
$eventApp = new EventScheduler();
$userType = $eventApp->User->getUserProfile("nameHere");
What I'm currently doing is this:
$eventApp = new EventScheduler();
//do some stuff here using the base class
$users = new User();
$userType = $users->getUserProfile("nameHere");
But as I add more child classes, I don't want to have to instantiate every extended class like I did there, I'd like to have them all grouped under the base object, rather than having each extended class in it's own object.
What you want to do is
$users = new User();
//do some stuff here using the base class
// ie $users->someFunctionFromEventScheduler();
$userType = $users->getUserProfile("nameHere");
This is a good reason to start reading up on common design patterns in OOP. There are plenty of good resources for this online and a quick google search will yield plenty of results and examples mostly hosted on github.
The specific pattern I believe you are looking for is the mediator pattern (Mediator pattern example in PHP). Rather than extending a class, as you are doing in your example, the mediator pattern is useful when you want an instance of a class that has access to many other classes that can all communicate with each other through one base class. The premise is that 'One good friend is better than many acquaintances.`.
An example for you: (interfaces are incredibly useful here as they define specific characteristics that are required in each of the classes)
/**
* Interface Mediator
*/
interface Mediator {
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated);
/**
* #param $key
* #return Mediated
*/
public function getAttached($key);
}
/**
* Interface Mediated
*/
interface Mediated {
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator);
/**
* #return Mediator
*/
public function getMediator();
}
Now we need a base mediator class, I'll use your event scheduler example. Notice that it implements the Mediator interface and must, as a result implement the methods the interface requires.
/**
* Class EventScheduler
*/
class EventScheduler implements Mediator {
/**
* A collection of mediated instances.
*
* #var array
*/
protected $mediated = [];
/**
* #param string $key
* #param Mediated $mediated
* #return void
*/
public function attach($key, Mediated $mediated)
{
// So upon attaching a mediated instance we can build the two
// way binding in one place using the key as the identifier.
// First we set $this on the mediated instance.
$mediated->setMediator($this);
// Then we add this instance to our mediated array inside this instance
$this->mediated[$key] = $mediated;
}
/**
* #param $key
* #return Mediated
*/
public function getAttached($key)
{
return $this->mediated[$key];
}
}
Now we can setup a mediated instance. That can be attached to the mediator. Notice it implements the Mediated interface.
/**
* Class User
*/
class User implements Mediated {
/**
* #var Mediator
*/
protected $mediator;
/**
* #param Mediator $mediator
* #return void
*/
public function setMediator(Mediator $mediator)
{
$this->mediator = $mediator;
}
/**
* #return Mediator
*/
public function getMediator()
{
return $this->mediator;
}
}
You can create as many of the mediated instances as you like and attach them to the Mediator instance. Bear in mind that this isn't a specific mediator, in that many instances can be attached, in most cases it's better to be explicit with which classes can be attached rather than allowing dynamic registration by a key.
$scheduler = new EventScheduler();
$user = new User();
$scheduler->attach('user', $user);
// Now we know that we can get the mediator from the User class
$user->getMediator();
// We can also get the User from the mediator itself.
$scheduler->getAttached('user');
As you attach more classes you'll notice that each of them can use their mediator to get instances of the other attached classes, this is where the concept of one good friend comes from.
This is just an example and not fully featured for brevity, but should give you a good idea why using common design patterns will help you enormously and is a very good place to start when learning OOP if you want to develop good habits.