ZF2 Controller actions execute common code - php

Apologies if this is a basic question but I have attempted to find an answer in the ZF2 documentation and I can't find a clear answer.
I have a set of Controllers each containing a set of actions. I would like each action to check for a certain parameter being present in the request. What is the best to ensure that this gets done for all actions such that if I add a new action it will automatically be catered for too?
Many thanks for your help.

You could extend the AbstractActionController and add your custom methods or override existing ones to your needs.
use Zend\Mvc\Controller\AbstractActionController;
abstract class AbstractCustomController extends AbstractActionController
{
//custom methods
}
once That is done you then simply access your custom controller trough namespaces and extend it with the new controllers.
<?php
namespace Custom\Controller;
use Zend\View\Model\ViewModel;
use Custom\Controller\AbstractCustomController;
class NewController extends AbstractUserController {
//new controller which has access to methods from extented AbstractController
}
You didnt specify on what exactly you are checking on. But using a MVC Event might be cleaner and easier to implement. You'll simply add some functionality to your module.php for example:
public function onBootstrap(MvcEvent $e) {
//check request
}
This is just an example and may not work though. Depending on the Order of events the request may not be filled at this given point and you'd be forced to use another one.
I once used the onDispatch(\Zend\Mvc\MvcEvent $e) provided by the AbstractActionController for a task that was a little bit similar to yours. But then again if you could be more specific we'd probably could give you a more specific answer.
best regards and good luck!
Edit: Regarding your question in the comment you could listen to the routing event and get your params there.
//Module.php within your onBootstrap Method
$application = $e->getApplication();
$eventManager = $application->getEventManager();
$eventManager->attach('route', function(MvcEvent $mvcEvent) {
$params = $mvcEvent->getRouteMatch()->getParams();
echo '<pre>'; print_r($params); exit;
}

Related

Laravel Service as Controller - working with multiples controllers

I'am a Brazilian developer, so... sorry for my limited English right away.
Well, in fact my problem is more a convention problem because until now I hadn't use services with Laravel (my apps were that simple so far).
I read about it before ask this question, but nothing helped with this specific situation. I'll try to describe in a objective way.
before that, just a comment: I know about the mistake using just controllers in these example. The ask is really about that mistake.
Well, the actual structure is:
abstract class CRUDController extends Controller {
protected function __construct($data, $validatorData) {
// store the data in a attribute
// create with Validator facade the validation and store too
}
abstract protected function createRecord();
protected function create() {
try {
// do the validation and return an Response instance with error messages
// if the data is ok, store in the database with models
// (here's where the magic takes place) in that store!
// to do that, calls the method createRecord (which is abstract)
$this->createRecord();
// return a success message in an Response instance
}
catch(\Exception $e) {
// return an Response instance with error messages
}
}
}
class UserController extends CRUDController {
public function __construct($data) {
parent::__construct($data, [
'rules' => [
// specific user code here
],
'messages' => [
// specific user code here
],
'customAttributes' => [
// specific user code here
]
]);
}
protected function createRecord() {
$user = new UserModel();
// store values here...
$user->save();
return $user;
}
}
// here's the route to consider in that example
Route::post('/user', 'WebsiteController#register');
class WebsiteController extends Controller {
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function register() {
$user = new UserController();
$user->create($this->request);
// here's the problem: controller working with another controller
}
}
class UserAPIController extends Controller {
// use here the UserController too
}
and many other classes that extends CRUDController in the same way...
What I want
I want to create a controller (called here as CRUDController) to reuse methods like the pattern says (create, read, update and delete).
To be really objective here I'll use the create method as an example.
With the code above it seems clear the purpose? I think so... all my controllers have that code of validation equal and reusable. That's the thing.
Besides that, I want to my route of website call another controller (UserController) to store new users... but in the same way, I'll create an API that uses the same controller in the same way (with validations etc). That's the purpose of Responses in the CRUDController (I'll read them in the WebSiteController to resolve what to do, like show a view and in the other hand with the API I'll basically return the Response.
My real problem
Convention and pattern. The MVC pattern is broken here. Controller calling another controller is wrong and I know that.
I want to know what thing I should use! Services? Is that right? I see a lot (really) of examples of services but nothing like that, working with models and reusing code, etc. I never use Services but I know how to use, but I don't know if it's right to these cases.
I really hope that someone can help here and sorry once again for the mistakes with the English. Thanks a lot.
You're calling the CRUD controller a controller but it does not behave as an MVC controller. At best it's just a helper class. You could always do this:
abstract class CRUDManager {
//As you had the CRUDController
}
class UserManager extends CRUDManager {
//As you had the UserController
}
In your AppServiceProvider:
public function boot() {
$app->bind(UserManager::class, function ($app) {
return new UserManager(request()->all()); //I guess that's what you need.
});
}
Whenever you need to use it you can do:
public function register(UserManager $user) {
$user->create();
}
Now one thing to point out. It's not a good idea to initialise the request in the constructor. You should use dependency injection in controller methods. I don't even know if the request is available when the controller is being constructed (I know the session is not). The reason why I say this is that the middleware runs after the controller is constructed and therefore the request may be modified when the controller method is called.
Another note: If you did the original solution because you needed to use certain controller methods, then you can just use the corresponding traits (because the controller itself does not really have many method). I'm guessing a trait like ValidatesRequests would be one you'd need to use.
I'll answer my own question. I use a pattern called Repository Pattern to resolve the problem (or I try to use, because it's the first time using this pattern: maybe I don't use in the right way in every steps).
Files structure
Controllers
UserController.php
Models
UserModel.php
Providers
UserRepositoryServiceProvider.php
Repositories
RepositoryInterface.php
Repository.php
User
UserRepositoryInterface.php
UserRepository.php
Traits
InternalResponse.php
With that structure I did what I wanted in my question without working just with controllers.
I create a trait called InternalResponse. That trait contains a few methods that receive a transaction, validate if it's the case and then return a Response (called "internal" in my logic because the controller will read and maybe change the Response before return it in the end).
The Repository class, which is abstract (because another class must extend it to make sense to use. In this case the class UserRepository will extend...), uses the Trait mentioned.
Well, with it in mind, it's possible to know that the UserController uses the UserRepositoryInterface, that provides an object UserRepository: because the UserRepositoryServiceProvider register this with that interface.
I think there's no need to write code here to explain, because the problem is about an pattern, and these words explain well the problem (in the question) and the resolution with this answer here.
I'll write here a conclusion, I mean, the files structure with comments to explain a little bit more, to end the answer.
Conclusion: Files structure with comments
Controllers
UserController.php
// the controller uses dependency injection and call methods of
// UserRepository, read and changes the Response receveid to finally
// create the final Response, like returning a view or the response
// itself (in the case it's an API controller)
Models
UserModel.php
// an normal model
Providers
UserRepositoryServiceProvider.php
// register the UserRepositoryInterface to
// return a UserRepository object
Repositories
RepositoryInterface.php
// the main interface for the Repository
Repository.php
// the main repository. It's an abstract class.
// All the others repositories must extend that class, because
// there's no reason to use a class Repository without an Model
// to access the database... That class share methods like create,
// read, update and delete, and the methods validate and transaction
// too because uses the trait InternalResponse.
User
UserRepositoryInterface.php
// the interface for UserRepository class
UserRepository.php
// that class extend Repository and uses the UserModel
Traits
InternalResponse.php
// trait with methods like validate and transaction. the method
// validate, read and validate the data receveid for the methods
// create and update. and all the CRUD methods uses the method
// transaction to perform the data to the database and return a
// response of that action.
That's what I do and like I said before, I don't know if it's a hundred percent correct in reference to Repository Pattern.
I hope this can help someone else too.
Thanks for all.

Laravel 4 Add Method to Class (IoC / Namespaces)

I'm trying to figure out how to add a method to a class in a Laravel package, so that all controllers and models that call that class can access the new method. How do I replace this class in the IoC?
This is the package in question, Angel CMS. The package is my creation, so I can modify it if we need to add aliases or anything to accomplish this.
Let's say I want to add a method to this class:
vendor/angel/core/src/models/PageModule.php
Okay, so I copy the class file to here:
app/models/PageModule.php
And then I modify the copied file, adding a namespace and the desired custom_function method:
<?php namespace MyModels;
use Eloquent;
class PageModule extends Eloquent {
protected $table = 'pages_modules';
public static function custom_function()
{
return 'It works!';
}
}
As you can see, I am using the MyModels namespace here.
Then, I run a composer dump-autoload.
Next, I open up my app/routes.php and register the binding and set up a test route:
App::bind('PageModule', function($app) {
return new \MyModels\PageModule;
});
Route::get('test-binding', function() {
return PageModule::custom_function();
});
But, when visiting the test route, I always receive the same error that the method is undefined.
What am I doing wrong here? Thank you in advance for any help.
To Clarify:
I am attempting to replace the class application-wide so that all other classes (controllers/models/etc.) that call PageModule will have access to the custom_function method. Thanks.
To be honest, I'm pretty new to all this IoC, dependency inversion/injection concept too. But I think I've gone through the same struggle before. What I would do, as much as my knowledge allows, is...
Add a constructor to src/controllers/admin/AdminPageController.php:
protected $pageModule;
public function __construct(PageModule $pageModule)
{
$this->pageModule = $pageModule;
}
Then where you did $module = new PageModule in the same file. You replace it with:
$module = $this->pageModule;
The two modifications above makes use of Laravel's IoC to allow injecting a different PageModule object into your controller, instead of strictly creating PageModule in your code.
Now at this point Laravel should know that when it constructs the AdminPageController, it should create a PageModule and inject into the controller for you.
Since your controller now expects a PageModule class, you can no longer do class PageModule extends Eloquent in your app anymore, because even though the name is the same, PHP does not think that it is! You'll need to extend it:
So let's rename your app/models/PageModule.php to app/models/CustomPageModule.php, and in the file change the class to:
class CustomPageModule extends \PageModule {
Up to this point, you also have a CustomPageModule class that is a child of your package's PageModule. All you need to do now is to let Laravel knows that if any controllers ask for PageModule, it should serve the controller with your MyModels\CustomPageModule instead.
So at the top of your app's routes.php file:
App::bind('PageModule', 'MyModels\CustomPageModule');
Your AdminPageController should now be using your CustomPageModule and can use whatever public methods that are in there!
I'm expecting to be editing this answer heavily since this will be quite a long discussion. My first try at answering above isn't the best code you can write, but I hope it takes the least amount of edit to the original code, and then we can work up from there.
Or fast track by reading up articles like http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories
You probably have a alias for the PageModule facade, you should override this alias using your class \MyModels\PageModule in your app/config/app.php file.
Be careful, it seems like you are overwriting the PageModule class instead of extending it. You should probably extend the parent class instead of Eloquent.

Extend Laravels Router class (4.1)

I would like to extend Laravels Router class (Illuminate\Routing\Router) to add a method I need a lot in my application.
But sadly I can't get this to work. I already extended other classes successfully so I really have no idea where my wrong thinking comes from.
Anyway, right into the code:
<?php
namespace MyApp\Extensions;
use Illuminate\Routing\Router as IlluminateRouter;
class Router extends IlluminateRouter
{
public function test()
{
$route = $this->getCurrentRoute();
return $route->getParameter('test');
}
}
So as you see I want to get the parameter set by {test} in routes.php with a simple call like:
Router::test();
Not sure how to go on now. Tried to bind it to the IOC-Container within my ServiceProvider in register() and boot() but I got no luck.
Whatever I try I get either a constructor error or something else.
All solutions I found are too old and the API has changed since then.
Please help me!
edit:
I already tried binding my own Router within register() and boot() (as said above) but it doesn't work.
Here is my code:
<?php
namespace MyApp;
use Illuminate\Support\ServiceProvider;
use MyApp\Extensions\Router;
class MyAppServiceProvider extends ServiceProvider {
public function register()
{
$this->app['router'] = $this->app->share(function($app)
{
return new Router(new Illuminate\Events\Dispatcher);
}
// Other bindings ...
}
}
When I try to use my Router now I have the problem that it needs an Dispatcher.
So I have to do:
$router = new Router(new Illuminate\Events\Dispatcher); // Else I get an exception :(
Also it simply does nothing, if I call:
$router->test();
:(
And if I call
dd($router->test());
I get NULL
Look at: app/config/app.php and in the aliases array. You will see Route is an alias for the illuminate router via a facade class.
If you look at the facade class in Support/Facades/Route.php of illuminate source, you will see that it uses $app['router'].
Unlike a lot of service providers in laravel, the router is hard coded and cannot be swapped out without a lot of work rewiring laravel or editing the vendor source (both are not a good idea). You can see its hardcoded by going to Illuminate / Foundation / Application.php and searching for RoutingServiceProvider.
However, there's no reason i can think of that would stop you overriding the router class in a service provider. So if you create a service provider for your custom router, which binds to $app['router'], that should replace the default router with your own router.
I wouldn't expect any issues to arise from this method, as the providers should be loaded before any routing is done. So overriding the router, should happen before laravel starts to use the router class, but i've not this before, so be prepared for a bit of debugging if it doesn't work straight away.
So I was asking in the official Laravel IRC and it seems like you simply can't extend Router in 4.1 anymore. At least that's all I got as a response in a pretty long dialogue.
It worked in Laravel 4.0, but now it doesn't. Oh well, maybe it will work in 4.2 again.
Other packages suffer from this as well: https://github.com/jasonlewis/enhanced-router/issues/16
Anyway, personally I'll stick with my extended Request then. It's not that much of a difference, just that Router would've been more dynamic and better fitting.
I'm using Laravel 4.2, and the router is really hard coded into the Application, but I extended it this way:
Edit bootstrap/start.php, change Illuminate\Foundation\Application for YourNamespace\Application.
Create a class named YourNamespace\Application and extend \Illuminate\Foundation\Application.
class Application extends \Illuminate\Foundation\Application {
/**
* Register the routing service provider.
*
* #return void
*/
protected function registerRoutingProvider()
{
$this->register(new RoutingServiceProvider($this));
}
}
Create a class named YourNamespace\RoutingServiceProvider and extend \Illuminate\Routing\RoutingServiceProvider.
class RoutingServiceProvider extends \Illuminate\Routing\RoutingServiceProvider {
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function($app)
{
$router = new Router($app['events'], $app);
// If the current application environment is "testing", we will disable the
// routing filters, since they can be tested independently of the routes
// and just get in the way of our typical controller testing concerns.
if ($app['env'] == 'testing')
{
$router->disableFilters();
}
return $router;
});
}
}
Finally, create YourNamespace\Router extending \Illuminate\Routing\Router and you're done.
NOTE: Although you're not changing the name of the class, like Router and RoutingServiceProvider, it will work because of the namespace resolution that will point it to YourNamespace\Router and so on.

Controller specific layout in ZendFramework 2

I have a module in my zendframework 2 application which contains two controllers.
I want to set a different layout for one of the controller's actions.
Is there a way to set it inside module config file?
P.s: I just tried to set it inside controller's __CONSTRUCT method using the following commands but it just didnt worked!
$event = $this->getEvent();
$event->getViewModel()->setTemplate('layout/MYLAYOUT');
But if i use the above commands inside each action of my controller it just works fine.
See akrabat's examples for a number of nice ways that layouts, views, etcetera can be tweaked easily.
Specifically what you're looking for can be found on his github here.
Here is a cut-paste of the controller's action method that sets/uses the alternate layout:
public function differentLayoutAction()
{
// Use a different layout
$this->layout('layout/different');
return new ViewModel();
}
Edit: It looks like akrabat has an example that says Change the layout for every action within a module, which might give the best pointers for setting the layout in the config; but I just had a look at the code, and the example is currently unfinished, it's not changing the layout.
I can just point you into the right direction, since currently i'm unable to open a sample project. Evan Coury has posted a method for Module specific layouts. See the following links:
Module Specific Layouts in Zend Framework 2
<?php
namespace MyModule;
use Zend\ModuleManager\ModuleManager;
class Module
{
public function init(ModuleManager $moduleManager)
{
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
// This event will only be fired when an ActionController under the MyModule namespace is dispatched.
$controller = $e->getTarget();
$controller->layout('layout/alternativelayout');
}, 100);
}
}
Now how would this help you?: Well, $controller should have both the called controller and action stored. I'm sure you can check the $controller for the called action and then assign the layout accordingly.
I'm sorry i can currently only hint you into the direction, but i'm sure this can get you started.
#Sam's answer pretty much answers the question. As stated it just needs a check on which controller is called, which can be done like this:
<?php
namespace MyModule;
use Zend\ModuleManager\ModuleManager;
class Module
{
public function init(ModuleManager $moduleManager){
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
$controller = $e->getTarget();
if ($controller instanceof Controller\AltLayoutController) {
$controller->layout('layout/alternativelayout');
}
}, 100);
}
I

MVC Optimization question Framework Zend

Just a quick logical question.
I have 2 Zend Controllers namely Merchant and Account. Merchant Controller has an action called editAction. My question is whether it is possible to have same action in Account as well without duplicating the code. I managed to create a include file for the view but I like to have a best way to manage my code. I currently have an idea of having an Helper class and invoke that helper class to both these Controllers. But I know someone would have got better solution than this. Please help me.
Thank you
The simplest solution would be to extend Zend_Controller_Action into your own base class and put editAction() into that. For example, assuming you have model classes named 'Account' and 'Merchant':
abstract class My_Controller_Action extends Zend_Controller_Action
{
protected $_modelName;
public function editAction()
{
$model = new $this->_modelName();
// Do your editing here.
}
}
class AccountController extends My_Controller_Action
{
protected $_modelName = 'Account';
}
class MerchantController extends My_Controller_Action
{
protected $_modelName = 'Merchant';
}
Keep in mind that this design implies that the code in editAction() would work for both Accounts and Merchants.
I think the best course of action would be to put majority of the logic inside your Models and keep your controllers lean. Your models can extend your own class which would have common operations in it.
You could also write your own class, it doesn't have to be a helper, and use it in the controllers to save the entity:
$saver = new My_Editing_Class();
$saver->edit("account",$this->getRequest()->getPost()); // Editing "account" with the POST data.
You then do your magic inside "My_Editing_Class".

Categories