Using toRoute() outside controller ZF2 - php

What I want:
If session is not set, I want to redirect to zf2.localhost/authUser/index
When I'm using toRoute in CreatorController everything works.
However, I want to use this method in several files. When I attempt to call toRoute in my SessionServiceController, I get the following error.
Error:
Url plugin requires that controller event compose a router; none found
Could you write me what I'm doing wrong?
Code:
CreatorController (zf2.localhost/creator/index)
<?php
namespace Creator\Controller;
use Zend\View\Model\ViewModel;
use Zend\Mvc\Controller\AbstractActionController;
use Creator\Controller\SessionServiceController;
class CreatorController extends AbstractActionController{
public function __construct(){
$this -> sessionService = new SessionServiceController();
}
public function indexAction(){
if($this-> sessionService ->checkSession()){
echo 'Witam';
}
}
}
SessionServiceController:
<?php
namespace Creator\controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Session\Container;
use Creator\Controller\SessionServiceController;
class SessionServiceController extends AbstractActionController {
const CONTAINER_SESSION_ID = 'usr_id';
public function __construct(){
$this -> session_id = new Container( self::CONTAINER_SESSION_ID);
}
public function checkSession(){
if (empty($this ->session_id->usr_id)){
//$this->redirect()->toUrl("zf2.localhost/authUser/index");
return $this->redirect()->toRoute('creator/default',
array('controller'=>'authUser', 'action'=>'index'));
}
if (!empty($this ->session_id->usr_id)){
return true;
}
}
}

You don't want to directly instantiate your controllers, use the ServiceManager. For toRoute to work, the Url helper needs a instance of the router. This is usually handled by the ControllerPluginManager when a controller is called from the ControllerManager.
But, beyond that, this logic is probably in the wrong place. If you're checking for authenticated users, you probably want to load and utilize zend's authentication service in an event before the controller is even loaded. I would guess the best place is somewhere in your ON_ROUTE event loop, but I can't speak to your specific application.
You will know if a user is logged in or not very early in the request chain, no reason to boot a controller. Take a peek at ZfcUser (https://github.com/ZF-Commons/ZfcUser), it already has versions of auth redirect listeners you can use as inspiration for your own (or you might find you just want to use that project for what you're doing)

Related

Call method of another controller in laravel

I have to call the method of another controller.
I use following code to make a call.
app('App\Http\Controllers\ApiUserController')->getList();
This is working fine.
But I want to try using use function so that I dont have to repeat all line
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
app('ApiUserController')->getList()
Did I made some mistake here?
Instead of using app function, you will need to go through OOP way like so:
use App\Http\Controllers\ApiUserController;
class MyMethods
{
public function index()
{
$apiUserController = new ApiUserController();
$apiUserController->getList();
However, as many people have mentioned here, it is not really the best practice to call a method of one controller from the another.
So if I were at your place, I would create a helper, register its alias in config and use that helper to get the list in both places.
I hope it helps
Calling controller from other controller or other objects is not a good practice. Here is a good article explaining why. Also "fat" controllers is less preferable than "thin" controllers.
You should define a service layer object with common logic and use it. Create a service object and register it with one of service providers.
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\YourUserService;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(YourUserService::class);
}
}
After that you can use your service in DI style.
use App\Services\YourUserService;
class MyMethods
{
protected $userService;
public function __construct(YourUserService $userService)
{
$this->userService = $userService;
}
public function index()
{
$this->userService->foo();
}
}
Why should I use dependency injection?
I agree with the answer Learner has given above, however, I wouldn't recommend it in terms of code organisation and testability.
Looking at the code, I can see that you need to get list of users and thats why you have to call the api user controller from another controller. However, you can easily extract the logic out to a service or even a trait.
if you were to use a trait then you could do some thing like following,
trait ApiUser {
public function getList()
{
// get the list for users from api
}
}
//Then you can simply use this trait any where you want,
class SomeController
{
// correct namespace for ApiUser trait
use ApiUser;
}
Another way of doing it, which i love to use again and again depending on the scenario; is to stick with the principle of coding to interface not to implementation. That would be some thing like follow.
interface ApiUserInterface
{
public function getList();
}
class ApiUser implements ApiUserInterface
{
public function getList()
{
// logic to get users from api
}
}
Make sure that when application requires the interface, it knows where to find its implementation. If you using Laravel, then you could register your interface to class in AppServiceProvider
Once that's done, you can use this service any where you want as a contract.
class OneController
{
protected $apiUserContract;
public function __construct(ApiUserInterface $apiUserContract)
{
$this->apiUserContract = $apiUserContract;
}
public function index()
{
// You can retrieve the list of the contract
$this->apiUserContract->getList();
}
}
// you could also just typehint the contact in method without requiring
// it in constructor and it will get resolved out of IOC i.e. container
class AnotherController
{
public function index(ApiUserInterface $apiUserContract)
{
// You can retrieve the list of the contract
$apiUserContract->getList();
}
}
Let me know if you need further explanation and hope it helps

Call a function on each http request

In my Apigility project I have different Rest resources, all of them extends my class ResourseAbstract and in there I extend the AbstractResourceListener as Apigility needs.
So for example my resource User:
<?php
namespace Marketplace\V1\Rest\User;
use ZF\ApiProblem\ApiProblem;
use Marketplace\V1\Abstracts\ResourceAbstract;
class UserResource extends ResourceAbstract
{
public function fetch($id)
{
$result = $this->getUserCollection()->findOne(['id'=>$id]);
return $result;
}
}
And ResourceAbstract:
<?php
namespace Marketplace\V1\Abstracts;
use ZF\Rest\AbstractResourceListener;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use ZF\ApiProblem\ApiProblem;
class ResourceAbstract extends AbstractResourceListener implements ServiceLocatorAwareInterface {
}
Now, I need to run a function each time an http request is made, if I query /user in my browser the UserResource class will get instantiated and so the ResourceAbstract, my "solution" to get something to run on each call was to use a constructor inside ResourceAbstract, and this "works":
function __construct() {
$appKey = isset(getallheaders()['X-App-Key']) ? getallheaders()['X-App-Key'] : null;
$token = isset(getallheaders()['X-Auth-Token']) ? getallheaders()['X-Auth-Token'] : null;
//some code
return new ApiProblem(400, 'The request you made was malformed');
}
The thing is I need to return an ApiProblem in some cases (bad headers on the http request), but as you know constructor function does not return parameters. Another solution will be to thrown an exception but in Apigility you are supposed to ise ApiProblem when there is an api problem. Is the constructor approach correct? How will you solve this?
Throwing an exception would be a solution, as long as you catch it on the parent portion of the code.
Are you using the ZEND MVC with your apigility project ?
If yes, you could consider hooking up a call that will be executed before the MVC does the dispatching.
If you want to look on the feasability of that approach, you can check that question asked on stackoverflow : Zend Framework 2 dispatch event doesn't run before action
I've not used this library, however it looks as if you can attach a listener to 'all' events by either extending the 'dispatch' method or adding your own event listener with high priority. The controller then listens for the returned 'ApiProblem'.
Attaching a listener is probably a better idea, in your custom class extending AbstractResourceListener (or from within it's service factory) you can then attach the event.
abstract class MyAbstractResource extends AbstractResourceListener
{
public function attach(EventManagerInterface $eventManager)
{
parent::attach($eventManager);
$eventManager->attach('*', [$this, 'checkHeaders'], 1000);
}
public function checkHeaders(EventInterface $event)
{
$headers = getallheaders();
if (! isset($headers['X-App-Key'])) {
return new ApiProblem(400, 'The request you made was malformed');
}
if (! isset($headers['X-Auth-Token'])) {
return new ApiProblem(400, 'The request you made was malformed');
}
}
}
The above would mean that any event triggered would first check if the headers are set, if not a new ApiProblem is returned.

Laravel Request::all() Should Not Be Called Statically

In Laravel, I'm trying to call $input = Request::all(); on a store() method in my controller, but I'm getting the following error:
Non-static method Illuminate\Http\Request::all() should not be called statically, assuming $this from incompatible context
Any help figuring out the best way to correct this? (I'm following a Laracast)
The error message is due to the call not going through the Request facade.
Change
use Illuminate\Http\Request;
To
use Request;
and it should start working.
In the config/app.php file, you can find a list of the class aliases. There, you will see that the base class Request has been aliased to the Illuminate\Support\Facades\Request class. Because of this, to use the Request facade in a namespaced file, you need to specify to use the base class: use Request;.
Edit
Since this question seems to get some traffic, I wanted to update the answer a little bit since Laravel 5 was officially released.
While the above is still technically correct and will work, the use Illuminate\Http\Request; statement is included in the new Controller template to help push developers in the direction of using dependency injection versus relying on the Facade.
When injecting the Request object into the constructor (or methods, as available in Laravel 5), it is the Illuminate\Http\Request object that should be injected, and not the Request facade.
So, instead of changing the Controller template to work with the Request facade, it is better recommended to work with the given Controller template and move towards using dependency injection (via constructor or methods).
Example via method
<?php namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller {
/**
* Store a newly created resource in storage.
*
* #param Illuminate\Http\Request $request
* #return Response
*/
public function store(Request $request) {
$name = $request->input('name');
}
}
Example via constructor
<?php namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller {
protected $request;
public function __construct(Request $request) {
$this->request = $request;
}
/**
* Store a newly created resource in storage.
*
* #return Response
*/
public function store() {
$name = $this->request->input('name');
}
}
use the request() helper instead. You don't have to worry about use statements and thus this sort of problem wont happen again.
$input = request()->all();
simple
Inject the request object into the controller using Laravel's magic injection and then access the function non-statically. Laravel will automatically inject concrete dependencies into autoloaded classes
class MyController()
{
protected $request;
public function __construct(\Illuminate\Http\Request $request)
{
$this->request = $request;
}
public function myFunc()
{
$input = $this->request->all();
}
}
The facade is another Request class, access it with the full path:
$input = \Request::all();
From laravel 5 you can also access it through the request() function:
$input = request()->all();
I thought it would be useful for future visitors to provide a bit of an explanation on what is happening here.
The Illuminate\Http\Request class
Laravel's Illuminate\Http\Request class has a method named all (in fact the all method is defined in a trait that the Request class uses, called Illuminate\Http\Concerns\InteractsWithInput). The signature of the all method at the time of writing looks like this:
public function all($keys = null)
This method is not defined as static and so when you try to call the method in a static context, i.e. Illuminate\Http\Request::all() you will get the error displayed in OP's question. The all method is an instance method and deals with information that is present in an instance of the Request class, so calling it in this way makes no sense.
Facades
A facade in Laravel provides developers with a convenient way of accessing objects in the IoC container, and calling methods on those objects. A developer can call a method "statically" on a facade like Request::all(), but the actual method call on the real Illuminate\Http\Request object is not static.
A facade works like a proxy - it refers to an object in the IoC container and passes the static method call onto that object (non-statically). For instance, take the Illuminate\Support\Facades\Request facade, this is what it looks like:
class Request extends Facade
{
protected static function getFacadeAccessor()
{
return 'request';
}
}
Under the hood, the base Illuminate\Support\Facades\Facade class uses some PHP magic, namely the __callStatic method to:
Listen for a static method call, in this case all with no parameters
Grab the underlying object from the IoC container using the key returned by getFacadeAccessor, in this case a Illuminate\Http\Request object
Dynamically call the method that it received statically on the object it has retrieved, in this case all is called non-statically on an instance of Illuminate\Http\Request.
This is why, as #patricus pointed out in his answer above, by changing the use/import statement to refer to the facade, the error is no longer there, because as far as PHP is concerned, all has been correctly called on an instance of Illuminate\Http\Request.
Aliasing
Aliasing is another feature that Laravel provides for convenience. It works by effectively creating alias classes that point to facades in the root namespace. If you take a look at your config/app.php file, under the aliases key, you will find a long list of mappings of strings to facade classes. For example:
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
// ...
'Request' => Illuminate\Support\Facades\Request::class,
Laravel creates these alias classes for you, based on your configuration and this allows you to utilise classes available in the root namespace (as referred to by the string keys of the aliases config) as if you're using the facade itself:
use Request:
class YourController extends Controller
{
public function yourMethod()
{
$input = Request::all();
// ...
}
}
A note on dependency injection
While facades and aliasing are still provided in Laravel, it is possible and usually encouraged to go down the dependency injection route. For example, using constructor injection to achieve the same result:
use Illuminate\Http\Request;
class YourController extends Controller
{
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function yourMethod()
{
$input = $this->request->all();
// ...
}
}
There are a number of benefits to this approach but in my personal opinion the greatest pro for dependency injection is that it makes your code way easier to test. By declaring the dependencies of your classes as constructor or method arguments, it becomes very easy to mock out those dependencies and unit test your class in isolation.
also it happens when you import following library to api.php file.
this happens by some IDE's suggestion to import it for not finding the Route Class.
just remove it and everything going to work fine.
use Illuminate\Routing\Route;
update:
seems if you add this library it wont lead to error
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
public function store(Request $request){
dd($request->all());
}
is same in context saying
use Request;
public function store(){
dd(Request::all());
}
I was facing this problem even with use Illuminate\Http\Request; line at the top of my controller. Kept pulling my hair till I realized that I was doing $request::ip() instead of $request->ip(). Can happen to you if you didn't sleep all night and are looking at the code at 6am with half-opened eyes.
Hope this helps someone down the road.
i make it work with a scope definition
public function pagar(\Illuminate\Http\Request $request)
{
//

Using multiple controllers inside of one view

I Need some advice, as I'm still a bit new to Laravel and MVC in general. I'm coding a small web application that presents some data on the page, fetched from a remote API. However, the page already has a controller to it. The other controller I will be using I'm hoping I can also reuse it for other pages. I'm pretty stuck here.
So the two controllers
HomeController.php
ApiController.php
The HomeController is the original controller, which gets the view file (home.blade.php), with some other data that's being loaded from the controller.
With the ApiController, I want to fetch the api (json) results, do some changes and then load those changes to the HomeController as well. The changes would be like an array of methods and such that's being loaded to the view.
So How can I load both controllers inside of the same view?
First of all controllers doesn't get loaded inside view instead, you should load a view from a controller and to make the remote request for an API call you don't need to use another controller but you may use it if you have other use of API and need a separate controller. The flow is something like this:
class HomeController extends BaseController {
public function index()
{
// make the api call/remote request
// modify the returned data
// load the view
}
}
Let's rewrite it:
class HomeController extends BaseController {
protected $apiService = null;
public function __construct(ApiService $apiService)
{
$this->apiService = $apiService;
}
public function index()
{
// make the api call/remote request
$apiData = $this->apiService->makeRequest();
// modify the returned data.... then...
// load the view
return View::make(...)->with('apiData', $apiData);
}
}
So, it seems clear that, you should use the API related process in a separate class as a service, maybe a model or a simple repository class and inject it to your HomeController then use it from the controller.
Do all the API stuffs in ApiService and call methods of that class from the HomeController, in this case you may implement the ApiServiceRepository as a concrete class by implementing an interface, i.e. ApiService. So, finally it could be like this:
interface ApiService {
public function makeRequest();
}
// Implement the interface in concrete class
class ApiServiceRepository implements ApiService {
public function makeRequest()
{
// $data = make remote request
// return $data
}
}
Use the class HomeController with __construct as given above and add a IoC binding like:
App::bind('ApiService', 'ApiServiceRepository');
So, you don't have to worry about the dependency injection in the constructor of your HomeController.
BTW, to use a method from another controller, for example ApiController from HomeController you may use something like this:
$apiController = App::make('ApiController');
// Call any method of "ApiController" class/object
$apidata = $apiController->makeCallToMethod();
You may also check this article for understanding the use of repository pattern in Laravel.

Implementing ServiceLocatorAwareInterface dependency for forward() class in a ZF2 service

EDITED (Code is updated and working for others)
For the overall idea of what's happening.
I'm trying to access post data from the view in the controller, without refreshing the page.
To do this I am executing the page controller by using a ViewHelper to call the Service below which then forwards back to the controller; afterwards I can manage the posted data in the page controller.
Everything works except the last step which is the forward(), I receive the error Call to undefined method AlbumModule\Service\postAlbumService::forward()
I understand I must implement the ServiceLocatorAwareInterface in order to use the forward() class, but what I've written doesn't seem to work.
<?php
namespace AlbumModule\Service;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class postAlbumService implements
ServiceLocatorAwareInterface
{
protected $services;
public function __construct() {
echo '<script>console.log("postAlbumService is Started")</script>';
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function test(){
$cpm = $this->getServiceLocator()->get('controllerpluginmanager');
$fwd = $cpm->get('forward');
echo '<script>console.log("postAlbumService TEST() is Started")</script>';
return $fwd->dispatch('newAlbum', array('action' => 'submitAlbum'));
}
}
It seems as though I'm just having a dependency issue with the forward() class, but I'm not sure what the issue is.
EDIT-
Here is how I am calling the postAlbumService from the viewHelper
<?php
namespace AlbumModule\View\Helper;
use Zend\View\Helper\AbstractHelper;
class invokeIndexAction extends AbstractHelper
{
protected $sm;
public function test()
{
$this->sm->getServiceLocator()->get('AlbumModule\Service\postAlbumService')->test();
}
public function __construct($sm) {
$this->sm = $sm;
}
}
Is there any way to call a specific class in the service being requested, after the dependencies are injected into the service?
You're doing a couple of things wrong and you're misunderstanding some things...
First of all, forward() is a ControllerPlugin. You'll gain access to this method by accessing said manager via the ServiceLocator. An example could be this:
$cpm = $serviceLocator->get('controllerpluginmanager');
$fwd = $cpm->get('forward');
return $fwd->dispatch('foo/bar');
Now, to get the ServiceLocator into any of your Service-Classes you need Dependency Injection. One of the ways is to implement the ServiceLocatorAwareInterface. The ServiceManager of ZF2 has so called Listeners. These Listeners check for implemented interfaces and stuff like this. Whenever it finds a match, it injects the required dependencies via the interfaces given functions. The workflow looks like this:
ServiceManager get('FooBar');
$ret = new FooBar();
foreach (Listener)
if $ret instanceof Listener
doInjectDependenciesInto($ret)
end
end
return $ret
Now what does this tell you. This tells you, that within the __construct() of any of your classes NONE of your required dependencies are actually there. They only get injected AFTER the class/service has been instantiated.
On a last side-note, the given code example doesn't really make much sense ;) No matter what ServiceAction i'd like to access, you'd always return me to the "newAlbum" action...

Categories