I have multiple controllers, with multiple methods, which all return views.
class PageController extends Controller {
public function index()
{
// do lots of stuff
return view('view.name', $lotsOfStuffArray);
}
public function list()
{
//...and so on
}
I now have the need to create an API, which performs much of the same logic as the methods above, but returns a JSON output instead:
class PageApiController extends Controller {
public function index()
{
// do lots of the same stuff
return $lotsOfStuffCollection;
}
public function list()
{
//...and so on
}
What is the best way to accomplish this without having to copy and paste code from one controller to the other?
I've tried placing a lot of the logic into traits and using them in my Eloquent models, but that still requires that I copy and paste code from controller to controller. I should also note its not viable to check expectsJson() and return a response accordingly as I have many, many methods.
Is it a good idea to have the logic stored in a parent class and then create a child controller that responds with a view and a child controller that responds with JSON?
You could abstract the logic to a service class. I have answered a similar question.
You have PageController, PageAPIController and PageService.
class PageService {
public function doStuff()
{
return $stuff;
}
}
class PageController extends Controller {
public function index()
{
$service = new PageService();
$stuff = $service->doStuff();
return $stuff;
}
}
class PageAPIController extends Controller {
public function index()
{
$service = new PageService();
$stuff = $service->doStuff();
return $stuff->toJSON();
}
protected function toJSON(){
//You could also abstract that to a service or a trait.
}
}
Related
I am new into Phalcon framework. I just got the basic idea about it. Every controller has methods with multiple specific actions. I wrote a huge indexAction method but now I want to break it down with multiple private method so that I can reuse those functionality. But when I try to create any method without action suffix, it returns error(Page Not Found). How can I break it down into multiple methods?
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function indexAction()
{
$this->someMethod();
}
public function someMethod()
{
//do your things
}
}
Controllers must have the suffix “Controller” while actions the suffix “Action”. A sample of a controller is as follows:
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function indexAction()
{
}
public function showAction($year, $postTitle)
{
}
}
For calling another method, you would use it straight forward
<?php
use Phalcon\Mvc\Controller;
class PostsController extends Controller
{
public function indexAction()
{
echo $this->showAction();
}
private function showAction()
{
return "show";
}
}
Docs.
What exactly do you want? The answer seems trivial to me.
class YourController extends Phalcon\Mvc\Controller
{
// this method can be called externally because it has the "Action" suffix
public function indexAction()
{
$this->customStuff('value');
$this->more();
}
// this method is only used inside this controller
private function customStuff($parameter)
{
}
private function more()
{
}
}
First off, I am relatively new to OOP as well as using an MVC, so I apologize if I do not use the right terminology or if I seem confused (because I am, haha)
I will start this off as basic as possible and if you need more information please let me know.
I am using Panique's MVC (Version HUGE)
https://github.com/panique/huge
So here goes nothing!
I have a base controller class that is setup like this...
Controller
<?php
class Controller {
public $View;
function __construct() {
$this->View = new View();
}
}
?>
With some extended controller classes like this (I will show two here)
IndexController
class IndexController extends Controller {
public function __construct() {
parent::__construct();
}
public function index() {
$this->View->render('index');
}
}
?>
ProfileController
class ProfileController extends Controller {
public function __construct() {
parent::__construct();
}
public function profile() {
$this->View->render('profile');
}
}
?>
My Question is, what does it take (if at all possible) to use an extended class method within another extended class method when both have the same parent class. Something Like...
<?php
class ProfileController extends Controller {
public function __construct() {
parent::__construct();
}
public function profile() {
$this->IndexController->index(); //Here I would like to use the method from the IndexController
}
}
?>
I have tried many of attempts to make this work but I think my lack of knowledge using OOP is hindering me. It seems that most everything I try except for a few cases, throws an error of...
Fatal error: Class 'IndexController' not found in blah/blah/ProfileController.php
I think if I could learn to target the extended class the right way I could manage the rest...hopefully ;)
There's no easy or elegant way to do that. You would need to instantiate the other class inside the class that needs to borrow the code, and that would probably cause many side effects in your app.
There may be other ways to do that, and that also depends on the possibilities / limitations of the framework, but thinking from the perspective of OOP in PHP, ignoring other factors, the best approach would be to implement the shared code in a method on Controller class:
<?php
class Controller {
public $View;
function __construct() {
$this->View = new View();
}
protected function myCustomCode() {
...
}
}
?>
And then call it normally on descendents:
<?php
class IndexController extends Controller {
public function __construct() {
parent::__construct();
}
public static function index() {
$this->myCustomCode();
$this->View->render('index');
}
}
?>
<?php
class ProfileController extends Controller {
public function __construct() {
parent::__construct();
}
public function profile() {
$this->myCustomCode();
...whatever...
}
}
?>
I don't see a better way of doing that. Besides, this is the natural way of OOP, where common stuff is up on class hierarchy (ancestors), never sideways or down (descendents). That helps keeping your code logical and easier to maintain.
Include the file of the class IndexController:
require_once('IndexController.php');
$this->controller = new IndexController();
Then invoke the method
$this->IndexController->index();
I have this route: Route::controller('/', 'PearsController'); Is it possible in Laravel to get the PearsController to load a method from another controller so the URL doesn't change?
For example:
// route:
Route::controller('/', 'PearsController');
// controllers
class PearsController extends BaseController {
public function getAbc() {
// How do I load ApplesController#getSomething so I can split up
// my methods without changing the url? (retains domain.com/abc)
}
}
class ApplesController extends BaseController {
public function getSomething() {
echo 'It works!'
}
}
You can use (L3 only)
Controller::call('ApplesController#getSomething');
In L4 you can use
$request = Request::create('/apples', 'GET', array());
return Route::dispatch($request)->getContent();
In this case, you have to define a route for ApplesController, something like this
Route::get('/apples', 'ApplesController#getSomething'); // in routes.php
In the array() you can pass arguments if required.
( by neto in Call a controller in Laravel 4 )
Use IoC...
App::make($controller)->{$action}();
Eg:
App::make('HomeController')->getIndex();
and you may also give params
App::make('HomeController')->getIndex($params);
You should not. In MVC, controllers should not 'talk' to each other, if they have to share 'data' they should do it using a model, wich is the type of class responsible for data sharing in your app. Look:
// route:
Route::controller('/', 'PearsController');
// controllers
class PearsController extends BaseController {
public function getAbc()
{
$something = new MySomethingModel;
$this->commonFunction();
echo $something->getSomething();
}
}
class ApplesController extends BaseController {
public function showSomething()
{
$something = new MySomethingModel;
$this->commonFunction();
echo $something->getSomething();
}
}
class MySomethingModel {
public function getSomething()
{
return 'It works!';
}
}
EDIT
What you can do instead is to use BaseController to create common functions to be shared by all your controllers. Take a look at commonFunction in BaseController and how it's used in the two controllers.
abstract class BaseController extends Controller {
public function commonFunction()
{
// will do common things
}
}
class PearsController extends BaseController {
public function getAbc()
{
return $this->commonFunction();
}
}
class ApplesController extends BaseController {
public function showSomething()
{
return $this->commonFunction();
}
}
if you were in AbcdController and trying to access method public function test() which exists in OtherController you could just do:
$getTests = (new OtherController)->test();
This should work in L5.1
I have a custom MVC PHP framework that has a router class, which calls a controller, which uses a model, then the controller presents the view, etc etc.
My problem is that I can't figure out technically how to allow variables to pass between the controller and the view, semantically. I could do a quick-and-dirty fix, but what I want to have is this for a controller:
class IndexController extends Controller{
var $name = "John"; // instance variable
}
And have this for a view:
<p> <?=$name?> </p>
My question is this:
How can I create a Controller->render() function, or something similar, that allows the view to access instance variables from the controller? and,
How can I do this without doing klutzy things like $data['view']['name'] = "John"; or having to write ten lines of code by default for any new controller I make. I want to do this so it's as DRY as possible.
Thanks.
Edit: FabioCosta's solution
I'm not sure I understand, so far I have my base controller like this:
<?php
class Controller{
public function __get($key){
if(isset($this->$$key)) return $this->$$key;
}
}
?>
My base view class looks like this:
<?php
class View{
public $controller;
public function render(){
$this->controller = $this;
}
?>
And I initialize from the router like this:
<?php
$controller = new IndexController();
$view = new IndexView();
$view->render();
?>
However, this doesn't work, and I know I'm doing something wrong.
Why not pass the controller that instantiates the view and use the __get magic method?
like so:
public function __get($key){
if(isset($this->$key)) return $this->$key;
}
Here is a working example View.php:
class View{
protected $_controller;
public function __construct(Controller $controller){
$this->_controller=$controller;
}
public function render(){
echo '<h1>Hello '.$this->_controller->name.'</h1>';
}
}
Controller.php
class Controller{
protected $name='fabio';
protected $_myView;
public function __get($key){
if(isset($this->$key)) return $this->$key;
}
public function __construct(){
$this->_myView=new View($this);
}
public function indexAction(){
$this->_myView->render();
}
}
And the router:
$c=new Controller();
$c->indexAction();
Controller should not be responsible for rendering output. That is something view instances should do. Rendering should happen outside the controller.
View should request data from model layer. Then, based on information it received, select the right template, assign data and render this template (or in some cases - group of templates).
Also , router should not initialize neither controllers nor views. Controller should be responsible only for processing the request.
I want to add more than one function to a ViewHelper. Usually there is one function named like the class and like the file name.
How can I add several functions into one ViewHelper?
E.g. like this:
class Zend_View_Helper_MyMenuHelper extends Zend_View_Helper_Abstract
{
public function Horizontal($parameter)
{
return "...";
}
}
echo $this->MyMenuHelper()->Horizontal($parameter);
Alex was on the right path, but missed something in his answer: the actual myMenuHelper() method has to return the view helper itself, for this to work:
class Zend_View_Helper_MyMenuHelper extends Zend_View_Helper_Abstract
{
public function myMenuHelper()
{
return $this;
}
public function horizontal() { ... }
// more methods...
}
And then, as mentioned:
echo $this->myMenuHelper()->horizontal();
Sometimes you don't want to pass through the main method of a view helper, although it's not that bad for some kinds of logic. In that case, use getHelper():
class Zend_View_Helper_MyMenuHelper extends Zend_View_Helper_Abstract
{
public function myMenuHelper()
{
// some logic, maybe the main one
}
public function horizontal()
{
// some other logic
}
}
The following examples bypass myMenuHelper() completely:
// in controller
$this->view->getHelper('MyMenuHelper')->horizontal();
// in view
$this->getHelper('MyMenuHelper')->horizontal();`
In some cases for example, I populate the view helper with some internal data in the controller, the call its main method directly in the view, which acts on that data.
// in controller
$this->view->getHelper('MyMenuHelper')->storeData($someArray);
// in view
$this->myMenuHelper(); // iterates over $someArray
try to start the function name with a lower case letter
class Zend_View_Helper_MyMenuHelper extends Zend_View_Helper_Abstract
{
public function horizontal($parameter)
{
return "...";
}
}
in the view:
echo $this->myMenuHelper()->horizontal($parameter);