Is splitting an index action into multiple ones a restful-friendly approach? - rest

I need to display two different index pages to two different user groups. For example, a regular user should see one page, and a privileged user - another one. I see two ways of approaching this issue:
One index action with conditionals:
public function index()
{
// view for privileged users
if(request()->user()->hasRole('privileged')){
return view('index_privileged');
}
// view for regular users
if(request()->user()->hasRole('regular')){
return view('index_regular');
}
return redirect('/');
}
Multiple actions:
public function index_privileged()
{
return view('index_privileged');
}
public function index_regular()
{
return view('index_regular');
}
Which approach is more "restful-friendly" and generally better?

I'm a big fan of light controllers. This might be a little overboard for a simple problem but if something like this pops up again, you'd already have everything all setup.
With that said, it might be best to create a PrivilegedUser class and a RegularUser class and give them both an index method which returns their respective views. Code them both to an interface UserInterface and make sure they both implement that.
Here is what those looked like in my test.
class RegularUser implements UserInterface
{
public function index()
{
return view('index_regular');
}
}
class PrivilegedUser implements UserInterface
{
public function index()
{
return view('index_privileged');
}
}
interface UserInterface
{
public function index();
}
Then you can add a listener which should run for the event Illuminate\Auth\Events\Login. Laravel will fire this event for you automatically when someone logs in. This goes into the file EventServiceProvider.php.
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\AuthLoginListener',
],
];
Now you can run php artisan event:generate to generate the new listener. Here is what my listener looks like, it should work for you.
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Foundation\Application;
class AuthLoginListener
{
/**
* Create the event listener.
*
* #param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle the event.
*
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
if ($event->user->hasRole('privileged')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\PrivilegedUser');
} else if ($event->user->hasRole('regular')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\RegularUser');
}
}
}
Essentially what this is doing is telling Laravel to load up a certain class based on the type of user that just logged in. The User instance is available through the Login object which was automatically passed in by Laravel.
Now that everything is setup, we barely have to do anything in our controller and if you need to do more things that are different depending on the user, just add them to the RegularUser or PrivilegedUser class. If you get more types of users, simply write a new class for them that implements the interface, add an additional else if to your AuthLoginListener and you should be good to go.
To use this, in your controller, you'd do something like the following...
// Have Laravel make our user class
$userRepository = App::make('App\Repositories\UserInterface');
return $userRepository->index()->with('someData', $data);
Or even better, inject it as a dependency.
use App\Repositories\UserInterface;
class YourController extends Controller
{
public function index(UserInterface $user)
{
return $user->index();
}
}
Edit:
I just realized I forgot the part where you wanted to return redirect('/'); if no condition was met. You could create a new class GuestUser (I know this sounds like an oxymoron) which implements UserInterface but instead of using the AuthLoginListener, I'd bind it in a service provider when Laravel boots. This way Laravel will always have something to return when it needs an implementation of UserInterface in the event it needs this class if no one is logged in.

Well, its more like a refactoring "issue" than a rest-friendly issue. Check this guideline and you can see that most of the things that makes an api friendly is concerned to the url.
But, lets answer what you are asking. The thing you wanna do is a refactoring method but it is not only the move method but something like the extract variable.
The second option would make the code more readable, either ways are right but the second is more developer friendly. It enhances the code readability from any developer. I would recommend using the second option.
Refactoring is never enough, but read something like this, it will help you a lot writing more readable codes.

Related

Proper way to access the container from a class not in a slim controller

I have a regular php class outside of a controller, so it doesn't benefit from automatic injection of container. I need to access the response object from that class, and I guess I should get it from the container.
What's the proper way to access it ? Just pass it as argument so the outside class can use it ? Is there a better way ?
You need to use middleware for that because the response object is immutable so "changing" it will not update the response which will be used by slim.
$app->add(function($request, $response, $next) {
if($shouldRedirect === true) {
return $response->withRedirect('myurl'); // do not execute next middleware/route and redirect
}
return $next($request, $response); // execute next middleware/ the route
});
For more information about middleware have a look at this.
If you need to send a subrequest, Slim provides such functionality. Use it carefully though, as in some situations its result is not obvious.
<?php
class MySortOfOutsideClass
{
/**
* If you need to send a subrequest, you have to access application instance,
* so let's inject it here.
*/
public function __construct(\Slim\App $app)
{
$this->$app = $app;
}
/**
* Method that makes a subrequest, and returns the result of it.
*/
public function myMethod()
{
if ($subRequestIsRequired) {
return $this->app->subRequest('GET', '/hello');
}
}
}

In Symfony where should I put entity dependant functions

I have this code in my controller, it takes 'procedure_type' from the request and checks to see if a ProcedureType with that name exists. If it does it uses the object, if not it creates a new ProcedureType, then return the new object to use.
// Check the typed in ProcedureType against existing types.
$procedureTypes = $entityManager->getRepository('IncompassSurgeryBundle:ProcedureType')->findBy(array('name' => $request->request->get('procedure_type'), 'vendor' => $vendorId));
if (empty($procedureTypes)) {
// Create Procedure Type
$procedureType = new ProcedureType();
$procedureType->setVendor($vendor)
->setName($request->request->get('procedure_type'))
->setCreated(new \DateTime())
->setUpdated($procedureType->getCreated());
$entityManager->persist($procedureType);
} else {
$procedureType = $procedureTypes[0];
}
I don't think this is the best way to do this, I'd like to move the code into a function, say checkProcedureType(), but I don't know where the best place is to put that. I don't think it could go in the Entity or Repository classes, and moving it to a private function in the controller doesn't feel right.
I'm sure there is a class type that I'm not aware of, that extends the Entity. Or maybe I should just put these functions in my entity classes.
Service are the answer to almost everything in Symfony 2. Create a service like this :
namespace Your\Bundle\Service;
class ProcedureService // Call this the way you want
{
protected $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
public function callMeTheWayYouWant($vendorId, $vendor)
{
// Check the typed in ProcedureType against existing types.
$procedureTypes = $this->entityManager->getRepository('IncompassSurgeryBundle:ProcedureType')->findBy(array('name' => $request->request->get('procedure_type'), 'vendor' => $vendorId));
if (empty($procedureTypes)) {
// Create Procedure Type
$procedureType = new ProcedureType();
$procedureType->setVendor($vendor)
->setName($request->request->get('procedure_type'))
->setCreated(new \DateTime())
->setUpdated($procedureType->getCreated());
$this->entityManager->persist($procedureType);
} else {
$procedureType = $procedureTypes[0];
}
// The rest of your code
}
}
In your services.yml file :
your_service:
class: Your\Bundle\Service\ProcedureService
arguments: [#doctrine.orm.entity_manager]
Then use it in your controller :
$this->get('your_service')->callMeTheWayYouWant($vendorId, $vendor);
If logic is somehow related to acessing database I always go for repository. However, if cases like yours, I tend to analyze it's dependency map.
Does your code repeats in some other method within same class, only?
If so, go for private method.
Is this part of code reused somewhere else but does not rely on some services?
You could externalize logic by creating separate class and static method which executes the code. Beware: Tends to get messy really quick
Finally, does your code rely on services/configuration?
Create a separate service, inject the services/configuration and invoke it's method. Adds a bit of overhead, if your abuse it, but you should be fine
Personally, in your example, I would go for private method, but that's just my opinion.

Using my own service with Laravel4

In my app, I was testing Google Directions API with ajax, but since I was just testing all the logic was in the routes.php file. Now I want to do things the proper way and have three layers: route, controller and service.
So in the routes I tell Laravel which method should be executed:
Route::get('/search', 'DirectionsAPIController#search');
And the method just returns what the service is supposed to return:
class DirectionsAPIController extends BaseController {
public function search() {
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
I created the service in app/libraries/Services/Directions and called it DirectionsSearchService.php and copied all the logic I developed in routes:
class DirectionsSearchService {
public function search($input = array()) {
$origin = $input['origin'];
$destination = $input['destination'];
$mode = $input['mode'];
// do stuf...
return $data;
}
}
I read the docs and some place else (and this too) and did what I was supposed to do to register a service:
class DirectionsAPIController extends BaseController {
public function search() {
App::register('libraries\Services\Directions\DirectionsSearchService');
$directionsSearchService = new DirectionsSearchService();
return $directionsSearchService->search(Input::all());
}
}
// app/libraries/Services/Directions/DirectionsSearchService.php
use Illuminate\Support\ServiceProvider;
class DirectionsSearchService extends ServiceProvider {
}
I also tried adding libraries\Services\Directions\DirectionsSearchService to the providers array in app/config/app.php.
However, I am getting this error:
HP Fatal error: Class
'libraries\Services\Directions\DirectionsSearchService' not found in
/home/user/www/my-app-laravel/bootstrap/compiled.php on line 549
What am I doing wrong? And what is the usual way to use your own services? I don't want to place all the logic in the controller...
2 main things that you are missing:
There is a difference between a ServiceProvider and your class. A service provider in Laravel tells Laravel where to go look for the service, but it does not contain the service logic itself. So DirectionsSearchService should not be both, imho.
You need to register your classes with composer.json so that autoloader knows that your class exists.
To keep it simple I'll go with Laravel IoC's automatic resolution and not using a service provider for now.
app/libraries/Services/Directions/DirectionsSearchService.php:
namespace Services\Directions;
class DirectionsSearchService
{
public function search($input = array())
{
// Your search logic
}
}
You might notice that DirectionsSearchService does not extend anything. Your service becomes very loosely coupled.
And in your DirectionsAPIController.php you do:
class DirectionsAPIController extends BaseController
{
protected $directionsSearchService;
public function __construct(Services\Directions\DirectionsSearchService $directionsSearchService)
{
$this->directionsSearchService = $directionsSearchService;
}
public function search()
{
return $this->directionsSearchService->search(Input::all());
}
}
With the code above, when Laravel tries to __construct() your controller, it will look for Services\Directions\DirectionsSearchService and injects into the controller for you automatically. In the constructor, we simply need to set it to an instance variable so your search() can use it when needed.
The second thing that you are missing is to register your classes with composer's autoload. Do this by adding to composer.json's autoload section:
"autoload": {
"classmap": [
... // Laravel's default classmap autoloads
],
"psr-4": {
"Services\\": "app/libraries/Services"
}
}
And do a composer dump-autoload after making changes to composer.json. And your code should be working again.
The suggestion above can also be better with a service provider and coding to the interface. It would make it easier to control what to inject into your controller, and hence easier to create and inject in a mock for testing.
It involves quite a few more steps so I won't mention that here, but you can read more in Exploring Laravel’s IoC container and Laravel 4 Controller Testing.

how to get view object in a plugin?

I have a controller action helper where i save a user object into the view (in an init function) like this:
Zend_Layout::getMvcInstance()->getView()->user = $user;
I'd like to access the object in a preDispatch method of a controller plugin so I don't have to look up the user in the database. I tried doing so like this:
$user = Zend_Layout::getMvcInstance()->getView()->user;
But it's returning a null object. I'm hoping its because I'm doing this wrong and not because I've programmed a catch 22 inside my login logic. Is there another way to access the object?
I think putting the following methods into your action helper may help you here.
private $user
public function init()
{
$this->user = new user();
}
public function preDispatch()
{
$user = $this->user;
//Do whatever you wish with the user object.
// although you probably don't need to do anything.
}
public function direct()
{
Zend_Layout::getMvcInstance()->getView()->user = $this->user;
//alternatively just return the user object or whatever you want to do
}
Then once your helper is registered you can simply do $this->_helper->helperName() in your controller to put the user object into the view.
Mathew WeirO'Phiney has a good explanation of action helpers on devzone. Especially the purpose of the direct() method.
Yes, good approach is to have a singleton class for current logged in user; in such a case it will be accessible anywhere - plugins, views, forms.

best practice on rendering several views based on same dynamic data

I would like to have some input on the following:
I would like some views to be systematically rendered (I can call $this->render from
my layout for these views) regardless of which controller/action is executed.
However the content used in these views is based on the same dynamically generated data and the code behind it is quite complex so I can't put the logic inside the views for obvious optimization/performance issues.
I could use $this->_helper->actionStack in each controller to call another controller in which data for the views would be prepared however I would like to do without modifying the existing controllers
I would be tempted to put something in the bootstrap since what I want is common to my application however I just don't know what to do.
That's what View Helpers are for.
In the View Helper you can fetch your data (through models or service layer) and prepare it for output.
<?php
class View_Helper_Foobar extends Zend_View_Helper_Abstract
{
protected $_data;
public function foobar()
{
if (null !== $this->_data) {
$this->_data = some_expensive_data_getter();
}
return $this;
}
public function __toString()
{
return $this->view->partial('foobar.phtml', array('data' => $this->_data));
}
}