I'm relatively new to the laravel framework and i noticed a pretty disturbing issue with laravel controllers. I dont know if its me but it doesnt seem to work at all. Lets say i have a controller and i want to split the logic contained in the method called when the request hits a route tied to the controller.
class SomeController extends BaseController
{
function doSomething()
{
$this->doSomethingExtra();
}
function doSomethingExtra()
{
return "Something Extra Done";
}
}
And lets say a have to route defined like so
Route::get('main/dashboard','SomeController#doSomething');
the second method called from the first never returns the string "Something Extra Done" to the browser. infact it returns an empty 200 response. However this seems to work when you return response from the doSomething() as usual. Anyone know why this is behaving this way? is there anyway to breakup a controller logic into several Methods that actually return responses to the client?
Change this
$this->doSomethingExtra();
to this.
return $this->doSomethingExtra();
Related
I'm struggling with how to do this correctly following best practices. It might be difficult to explain but i'll try my best here.
I have an external API I need to make very many different calls to. So what I did was creating a class in the App folder called Api.php for now. It's using Guzzle for API calls.
In the Controller for the view I create the Api object in the needed functions and call the corresponding function in the API class.
Controller
public function uploadDevice(Request $request)
{
## Validation etc is performed
// Calling the API
$api = new Api();
$api->uploadDevice();
}
Api.php
class Api
{
private $token;
public function __construct(){}
public function checkIfHasToken(){}
public function getTokenFromSession(){}
public function getFreshToken(){}
public function uploadDevice(){}
}
Some questions
The checkIfHasToken() needs to be called before every request. Should it be done in the constructor, first in each function doing API calls or directly from the Controller?
Exceptions : Where should I do the Try/catch etc ? Should it be done in the Api class where it's needed or in the Controller by calling each and every function from the API class and wrapping it in try/catch?
Redirects : I want to redirect back to the Route the request came from with every possible errors or success message included. So if I have a try/catch I want to redirect with the result of the catch included. Where to put this logic? Redirecting from the nested function does not seem to work. So then I'm back to calling each and every function in the Api class from the Controller one by one and handle the exceptions/errors/validations separately in the Controller?
Maybe I'm thinking too much about this or making it more complicated than it needs to be. Not sure anymore.
// Controller
public function __construct(ApiService $apiService)
{
$this->api = $apiService;
}
public function uploadDevice(Request $request)
{
// Ensure that the user has a token in a custom HTTP request or in a middleware somewhere
try {
$this->api->uploadDevice();
}
catch (Exception $exception){
return redirect()->back();
//You can include errors from $exception here.
}
}
// Service
class ApiService
{
public function uploadDevice()
{
return 'I did a thing';
}
}
Explaination
Laravel has many ways to do the same thing, it is all about what you need and how you want your application to scale.
Checking if a token is present or valid should be done in a middleware.
A try catch can be anywhere depending on how much you need to see in the exception, normally just in a controller is ok, but you can
do this in many ways. I personally like to make an event listener
for any http error.
Return redirect back from the controller will be fine to always redirect to the place that invoked the controller
The checkIfHasToken() needs to be called before every request. Should
it be done in the constructor, first in each function doing API calls
or directly from the Controller?
If it needs to be called for every request, I suggest making it middleware as it's made for this purpose.
Exceptions : Where should I do the Try/catch etc ? Should it be done
in the Api class where it's needed or in the Controller by calling
each and every function from the API class and wrapping it in
try/catch?
This depends, if you want to be able to control the output when an exception occurs then you probably want it in your controller. If you can program something to do when the exception occurs (return unsuccessful for instance), do it in a lower level (api).
Redirects : I want to redirect back to the Route the request came from
with every possible errors or success message included. So if I have a
try/catch I want to redirect with the result of the catch included.
Where to put this logic? Redirecting from the nested function does not
seem to work. So then I'm back to calling each and every function in
the Api class from the Controller one by one and handle the
exceptions/errors/validations separately in the Controller?
You can go back by returning redirect()->back() as the response, the best way to show errors would to include them somewhere. I suggest using session()->flash() for this. These calls can be made from the try/catch.
I'm trying to create my own little PHP-Framework just for fun and to learn.
But now I stuck with the View.
class Index extends Controller {
function __construct() {
parent::__construct();
$this->view->msg = 'This message is sended over the view.';
$this->view->content = 'This is the INDEX-Content.';
$this->view->render('index/index');
}
public function something() {
// do something
// and render it
$this->view->content = 'This is the content from something.'
}
So what I can do is to misuse the __destruct and render here my output. But I guess that is against the purpose of this method.
When I compare my intention with e.g. Zend Framework or Laravel they use e.g. an after() method to render a view.
But I do not understand which method can do this. The constructor is the first, the destructor the last and everything between it has to be called to work.
Are there any "magic" methods for this?
You should handle your HTTP I/O
This is how you can output
This is how a request is executed
This is how the action is triggerd
Sniff through the repo as much as you can, Kohana is a simple yet powerfull framework. (you can learn a thing or two)
You can do something like this in your main Controller class :
public function __call($method,$arguments) {
if(method_exists($this, $method)) {
call_user_func_array(array($this,$method),$arguments); //this is where the function is called
$this->render();
}
}
You can eliminate in hear constructors, destruct and other functions that you do not want to automatically render.
You can also have a variable in your main Controller class, autoRender set default to false and just set it to true when you want to produce a predefined output.
Also in the _call function, you can use the $method variable to have a predefined name for your view. Like for example lets say you would have a folder in your framework called Views and in there you would have a file called something.view_extension.
You can send to render like this : $this->render($method.'.view_extension');
Just a bulk idea you can work around. :)
I'm trying to return the result of a task from a subcontroller with the following url:
index.php?option=com_example&task=subctrl.test&format=json
but I keep getting the 500 View not found Error...
class ExampleControllersSubctrl extends JControllerForm
{
public function test()
{
$result= array("val1","val2");
echo json_encode($result);
}
}
I've tried naming the subcontroller file both Subctrl.php & Subctrl.json.php but neither worked. I believe I shouldn't need a view to render the result based on other SO posts I've read but maybe that is incorrect.
This setup will eventually be used to return an Ajax call when I get it working.
What am I doing wrong here?
Add an exit statement after the echo statement or Joomla will continue processing the component and will try to call a view. Since no view value was set, no view will be found and the system will redirect to an error page. Full code below:
class ExampleControllerSubctrl extends JControllerForm
{
public function test()
{
$result= array("val1","val2");
echo json_encode($result);
exit();
}
}
Joomla also some other methods that you can use such as call jexit() or JFactory::getApplication()->close(). The general idea is to get the application to stop here. Continuing is a waste.
Also, had to make sure the class name is set right. Middle work should be Controller not Controllers.
The problem is that you're extending JControllerForm which will try and guess the view for your form if one isn't provided.
On Joomla 2.5 you can change JControllerForm to JController and that will resolve the problem.
As you have a JSON controller that Joomla is routing you to via format=json you don't need an exit on your test() method either.
I'm using Zend_Test_PHPUnit_ControllerTestCase to test my controllers. This class provides various ways to test the rendered output, but I don't want to get my view scripts involved. I'd like to test my view's vars. Is there a way to access to the controllers view object?
Here is an example, what I'm trying to do:
<?php
class Controller extends Zend_Controller_Action
{
public function indexAction()
{
$this-view->foo = 'bar';
}
}
class ControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
public function testShowCallsServiceFind()
{
$this->dispatch('/controller');
//doesn't work, there is no such method:
$this->assertViewVar('foo', 'bar');
//doesn't work, end_Test_PHPUnit_ControllerTestCase has no getView method:
$this->assertEquals(
'bar',
$this->getView()->foo
);
}
}
If you really must assert against the view, get it with Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer')->view and assert against it.
The intention of Zend_Test however is that you would assert against the actual response, using an xpath query or something similar. this will allow you to fully test your application, instead of just a part of it.
if you simply assert that the view contains a var and that it is equal to a given thing, you are not really testing that it has been used in the right way.
1) Zend_Test_PHPUnit_ControllerTestCase::_resetPlaceholders() uses the singelton obtained in Zend_Registry::getInstance() and searches it for placeholders. Maybe you could mimic that behaviour.
2) Did you try $view = Zend_Layout::getMvcInstance()->getView() already? I haven't tested controllers yet but since the test cases includes singeltons, perhaps that wouldn't be such a far out guess.
In Zend, models are added to the view:
//In a controller
public function indexAction() {
//Do some work and get a model
$this->view->model = $model;
}
We can easily check that "model" exists in the view (I'm using simpletest for this):
//In a unit test
public function testModelIsSetInView() {
//Call the controllers index action
$this->assertTrue(isset($this->controller->view->model));
}
However, testing the "value" doesn't work as well:
//In a unit test
public function testModelValue() {
//Call the controllers index action
//Both of these return null, though I'd like to access them!
$this->assertNull($this->controller->view->model);
$this->assertNull($this->controller->view->__get('model'));
}
How do I get (or at least test) that the controller has set a valid model?
http://www.contentwithstyle.co.uk/content/unit-testing-controllers-with-zend-framework
So, the solution (at least the planned one at this time) is make a testable view that implements Zend_View_Interface. This will include a "get" method that returns objects passed to "__set". Then we'll hook up the controller to use this view during the test bootstrapping process.
Since this may not be the optimal approach, I'd still love to hear from others who have potential solutions.