I have the following event listener but want to set a viewVar from it and having some issues figuring out how. If I can't or shouldn't, what would be the best way when I need $products available to the View?
File is ./Products/Lib/Event/Products.php.
<?php
App::uses('CakeEventListener', 'Event');
class Products implements CakeEventListener {
public function implementedEvents() {
return array(
'View.beforeRender' => 'get_products',
);
}
public function get_products($event) {
$this->Product = ClassRegistry::init('Products.Product');
$products = $this->Product->find('all', array(
'fields' => array('Product.*', 'Content.title')
));
$this->set('products', $products);
}
}
Returns Fatal error: Uncaught Error: Call to undefined method Products::set().
You are subscribing to an event triggered by a View object, hence the subject of the event will be that object, and you can access it in your listener method via the event objects subject() method, like:
$event->subject()->set('products', $products);
See also
API > CakeEvent::subject()
API > View::render()
API > View::set()
Related
I've got an event listener in my project's Lib/Event directory and a model called QueueManagerJob.
My application dispatches an event at some point and I'd like to save an entry into my database through my model. When my event is dispatched, it runs a store method in the listener and it's here where I'm referencing my model and am trying to save it.
I've pulled the model in via the $uses array but I'm getting the following error:
Call to a member function set() on null
<?php
App::uses('CakeEventListener', 'Event', 'CakeLog');
class DispatchJobListener implements CakeEventListener {
public $uses = array('QueueManagerJob');
public function implementedEvents() {
return array(
'QueueManagerModule.QueueManagerJob.dispatchJob' => 'store'
);
}
/**
* Store the data
*/
public function store($event) {
$event->stopPropagation();
$job = [
'queue' => 'default',
'payload' => json_encode([]),
'attempts' => 0,
'reserved_at' => null,
'available_at' => date('Y-m-d H:i:s'),
'created_at' => date('Y-m-d H:i:s')
];
$this->QueueManagerJob->set($job);
// invalid
if (!$this->QueueManagerJob->validates()) {
// $this->QueueManagerJob->validationErrors;
// ...
}
// save the job
$this->QueueManagerJob->save($job);
}
}
Your class only implements an interface, there is no further logic that would make use of the $uses property.
The $uses property is only recognized out of the box by controllers, if you need to load models in other places, then you have to use ClassRegistry::init(), like:
App::uses('ClassRegistry', 'Utility');
// ...
$QueueManagerJob = ClassRegistry::init('QueueManagerJob');
Also note that App::uses() only accepts two arguments, the class name and the package name, the third argument in your example won't do anything!
My first controller is
class MatchesController extends AbstractActionController {
public function checkLogsAction() {
// $logs=new LogsController();
$logs=$this->getServiceLocator()->get('Admin\LogsController');
$logs->writeLogs("log data");
die();
}
Logs Controller
class LogsController extends AbstractActionController {
public function writeLogs($logData) {
$this->getServiceLocator()->get('Zend\Log\opta')->info($logData);
return true;
}
global.php
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
'Zend\Log\opta' => function ($sm) {
$fileName=date("Y-m-d");
$log = new Zend\Log\Logger();
$writer = new Zend\Log\Writer\Stream("./data/opta/$fileName");
$log->addWriter($writer);
return $log;
}
),
),
module.php
public function getServiceConfig() {
return array(
"factories"=>array(
'Admin\LogsController' => function ($sm) {
$logsController = new LogsController();
return $logsController;
},
I am getting this error:
Fatal error: Call to a member function get() on null
Please help me to solve the solution
Your Admin\LogsController extends AbstractActionController. But you do not use it as AbstractActionController!
An AbstractActionController is usuallay invoked by processing the (http) request, whereby the ZF2 application is going to route the request to a controller and executes an action method. During this processing, an instance of ServiceLocator/ServiceManager is passed to the controller. That is what you are missing. Hence, you try to call a method on a null object.
You can not simply instantiate an ActionController from another ActionController. (of course, it is possible, with a lot of afford). If you use it this way, you to make sure the new controller instance holds an instance of the ServiceLocator, request, response etc...
You should consider:
a) is Admin\LogsController really a AbstractActionController in your application? (I assume it is not, respectively your code example)
b) inject the ServiceLocator in to your custom object (LogsController), or a way cleaner: inject the logger instance.
Example:
public function getServiceConfig() {
return array(
'factories' => array(
'Admin\LogsController' => function ($sm) {
$logsController = new LogsController();
$logsController->setServiceLocator($sm); // you have to implement!
return $logsController;
},
);
}
Try and data to database and get an error.:
Uncaught Error: Call to a member function persist() on null in
public function addNewPostAction()
{
// Create new Post entity..
// $entityManager = $container->get('doctrine.entitymanager.orm_default');
$post = new Post();
$post->setTitle('Top 10+ Books about Zend Framework 3');
$post->setContent('Post body goes here');
$post->setStatus(Post::STATUS_PUBLISHED);
$currentDate = date('Y-m-d H:i:s');
$post->setDateCreated($currentDate);
$this->entityManager->persist($post);
$this->entityManager->flush();
}
UPDATE:
Error:
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for get
public function addNewPostAction()
{
// Create new Post entity..
// $entityManager = $container->get('doctrine.entitymanager.orm_default');
$post = new Post();
$post->setTitle('Top 10+ Books about Zend Framework 3');
$post->setContent('Post body goes here');
$post->setStatus(Post::STATUS_PUBLISHED);
$currentDate = date('Y-m-d H:i:s');
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$dm->persist($post);
$dm->flush();
}
From the 2 samples above, it is obvious you are trying to get doctrine `s entity manager.
1st sample:
$this->entityManager
probably the property $entityManager of the controller is not set, also from the commented code
$entityManager = $container->get('doctrine.entitymanager.orm_default');
it is obvious you are trying to get entity manager.
2nd sample:
$this->get('doctrine.odm.mongodb.document_manager');
I assume this is from a symfony example.
Anyway to get the doctrine manager in your controller, you have to inject it, change your controller constructor to accept it as an argument:
class IndexController extends AbstractActionController
{
private $doctrineManager;
public function __construct($doctrineManager)
{
$this->doctrineManager = $doctrineManager;
}
and then inject the doctrine manager to your controller factory in your module.config.php:
'controllers' => [
'factories' => [
Controller\IndexController::class => function ($container) {
return new Controller\IndexController(
$container->get('doctrine.odm.mongodb.document_manager')
);
},
// ...
],
],
Side note: the error "Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for get" is thrown because zend tries any undefined methods to resolve them to a plugin, eg. if you define a plugin with name myAwesomePlugin, you can access it in your action as:
$this->myAwesomePlugin();
I would like to create a question which has many surveys. In the questions Model:
public function surveys()
{
return $this->belongsToMany(Survey::class, 'survey__surveyquestions');
}
And in the controller when saving a new question:
private $questions;
public function __construct(QuestionsRepository $questions)
{
parent::__construct();
$this->questions = $questions;
}
public function store(Request $request)
{
$this->questions->create($request->all());
$this->questions->surveys()->attach($request->surveys);
return redirect()->route('admin.survey.questions.index')
->withSuccess(trans('core::core.messages.resource created', ['name' => trans('survey::questions.title.questions')]));
}
But I get the following error when it gets to the attach line:
(1/1) FatalErrorException Call to undefined method
Modules\Survey\Repositories\Eloquent\EloquentQuestionsRepository::surveys()
I notice the error mentions EloquentQuestionsRepository but I have added no methods in there so it's just an empty class:
class EloquentQuestionsRepository extends EloquentBaseRepository implements QuestionsRepository
{
}
QuestionRepository:
interface QuestionsRepository extends BaseRepository
{
}
As explained in the response to the main post - the constructor resolves the QuestionsRepository to instance of EloquentQuestionsRepository, which by the look of it is not what the store method needs.
What I would probably do is to make call to create method directly on the model and remove constructor all together - that is unless you need the instance of QuestionsRepository anywhere else in your controller:
public function store(Request $request)
{
$question = Question::create($request->all());
$question->surveys()->attach($request->surveys);
...
}
Also - I'm not sure passing $request->all() is the best thing to do - I'd probably use $request->only(...) or $request->all(...) specifying which items you want to get from the request rather than passing everything from the request to the create method.
On the other note - you could also use Form Request, which would validate data for your before passing it to the store method.
https://laravel.com/docs/5.5/validation#form-request-validation
With Symfony 2.8, this code $this->container is null.
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
class EntrarYPreregistroFormSubscriber implements EventSubscriberInterface, ContainerAwareInterface
{
use ContainerAwareTrait;
public function preSetData(FormEvent $event)
{
$l = $this->container->get('logger');
$l->notice('GOT LOGGER');
}
....
}
And my EntrarYPreregistroFormSubscriber service is configured as:
pmktconcursos.entrarypreregistro.form.subscriber:
class: PMKT\ConcursosBundle\EventListener\EntrarYPreregistroFormSubscriber
calls:
- [ setContainer,[ "#service_container" ] ]
I am getting exception at $l = $this->container->get('logger');
request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalErrorException: "Error: Call to a member function get() on a non-object" at /Users/vmarquez/Proyectos/GrupoPMKT/Promoticket/current/dev/chedraui1607/chedraui1607/src/PMKT/ConcursosBundle/Form/EventListener/EntrarYPreregistroFormSubscriber.php line 30 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalErrorException(code: 0): Error: Call to a member function get() on a non-object at /Users/vmarquez/Proyectos/GrupoPMKT/Promoticket/current/dev/chedraui1607/chedraui1607/src/PMKT/ConcursosBundle/Form/EventListener/EntrarYPreregistroFormSubscriber.php:30)"}
Am I missing something?
You are making Form Event Listener, so inside your Form Type you have something like $builder->addEventSubscriber(new EntrarYPreregistroFormSubscriber()); so you can see that Form Event Listeners works with a little difference that a regular Event Listener. As it is you who creates the object, so it is you should call setContainer($serviceContainer). To do that you should have service container inside you Form Type. To do that, you should pass it as option while creating your form in controller
// in controller
$object = ...;
$form = $this->createForm(new YourFormType(), $object, array('service_container' => $this->get('service_container')));
// in YourFormType
$listener = new EntrarYPreregistroFormSubscriber();
$listener->setContainer($options['service_container']);
$builder->addEventSubscriber($listener);
...
// in setDefaultOptions method
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
...
'service_container' => null,
));
}