1

Laravel 4: In the context of consume-your-own-api, my XyzController uses my custom InternalAPiDispatcher class to create a Request object, push it onto a stack (per this consideration), then dispatch the Route:

class  InternalApiDispatcher {
// ...
public function dispatch($resource, $method)
{
    $this->request = \Request::create($this->apiBaseUrl . '/' . $resource, $method);
    $this->addRequestToStack($this->request);

    return \Route::dispatch($this->request);
}  

To start with, I'm working on a basic GET for a collection, and would like the Response content to be in the format of an Eloquent model, or whatever is ready to be passed to a View (perhaps a repository thingy later on when I get more advanced). It seems inefficient to have the framework create a json response and then I decode it back into something else to display it in a view. What is a simple/efficient/elegant way to direct the Request to return the Response in the format I desire wherever I am in my code?

Also, I've looked at this post a lot, and although I'm handling query string stuff in the BaseContorller (thanks to this answer to my previous question) it all seems to be getting far too convoluted and I feel I'm getting lost in the trees.

EDIT: could the following be relevant (from laravel.com/docs/templates)?

"By specifying the layout property on the controller, the view specified will be created for you and will be the assumed response that should be returned from actions."

3 Answers 3

2

Feel free to mark this as OT if you like, but I'm going to suggest that you might want to reconsider your problem in a different light.

If you are "consuming your own API", which is delivered over HTTP, then you should stick to that method of consumption.

For all that it might seem weird, the upside is that you could actually replace that part of your application with some other server altogether. You could run different parts of your app on different boxes, you could rewrite the HTTP part completely, etc, etc. All the benefits of "web scale".

The route you're going down is coupling the publisher and the subscriber. Now, since they are both you, or more accurately your single app, this is not necessarily a bad thing. But if you want the benefits of being able to access your own "stuff" without resorting to HTTP (or at least "HTTP-like") requests, then I wouldn't bother with faking it. You'd be better off defining a different internal non-web Service API, and calling that.

This Service could be the basis of your "web api", and in fact the whole HTTP part could probably be a fairly thin controller layer on top of the core service.

It's not a million miles away from where you are now, but instead of taking something that is meant to output HTTP requests and mangling it, make something that can output objects, and wrap that for HTTP.

Sign up to request clarification or add additional context in comments.

1 Comment

This is the kind of big-picture assistance I needed. Although I had tackled my original question with a config flag, the overall structure seemed wrong. But let me see if I have it right now. The ctrlrs, models, views, etc in the app dir will manage the UI for my app, which can still have RESTful urls, and those ctrlrs can call the internal service api, which will have matching RESTful urls and can be the basis of a separate web api. But now I find myself in a thicket of namespaces. Could you maybe provide me a sketch of an appropriate directory structure? Thanks.
1

Here is how I solved the problem so that there is no json encoding or decoding on an internal request to my API. This solution also demonstrates use of route model binding on the API layer, and use of a repository by the API layer as well. This is all working nicely for me.

Routes:

Route::get('user/{id}/thing', array(
    'uses' => 'path\to\Namespace\UserController@thing',
    'as' => 'user.thing'));
//...
Route::group(['prefix' => 'api/v1'], function() 
{
    Route::model('thing', 'Namespace\Thing');
    Route::model('user', 'Namespace\User'); 

    Route::get('user/{user}/thing', [
    'uses' => 'path\to\api\Namespace\UserController@thing',
    'as' => 'api.user.thing']);
//...

Controllers:

UI: UserController@thing
public function thing()
{
    $data = $this->dispatcher->dispatch('GET', “api/v1/user/1/thing”)
        ->getOriginalContent();  // dispatcher also sets config flag...
    // use $data in a view;
}

API: UserController@thing

public function thing($user)
{
    $rspns = $this->repo->thing($user);

    if ($this->isInternalCall()) { // refs config flag 
        return $rspns;
    }
    return Response::json([
    'error' => false,
    'thing' => $rspns->toArray()
], 200);

Repo:

public function thing($user)
{   
    return $user->thing;
}

Comments

0

Here is how I achieved it in Laravel 5.1. It requires some fundamental changes to the controllers to work.

Instead of outputting response with return response()->make($data), do return $data.

This allows the controller methods to be called from other controllers with App::make('apicontroller')->methodname(). The return will be object/array and not a JSON.

To do processing for the external API, your existing routing stays the same. You probably need a middleware to do some massaging to the response. Here is a basic example that camel cases key names for the JSON.

<?php
namespace App\Http\Middleware;
use Closure;
class ResponseFormer
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        if($response->headers->get('content-type') == 'application/json')
        {
            if (is_array($response->original)) {
                $response->setContent(camelCaseKeys($response->original));
            }
            else if (is_object($response->original)) {
                //laravel orm returns objects, it is a huge time saver to handle the case here
                $response->setContent(camelCaseKeys($response->original->toArray()));
            }
        }
        return $response;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.