selahattinunlu / laravel-api-query-builder

Laravel & Lumen Api Query Builder Package
332 stars 66 forks source link

Proposal - Use "array of params" instead $request #15

Closed robsontenorio closed 7 years ago

robsontenorio commented 7 years ago

Problem

Sometimes i need to call model`s methods, that uses a QueryBuilder instance. So , i "can not trust" on $request object, in this case. Because, not all parameters in current querystring are applied for both QueryBuilder instances.

Proposal

This is my current implementation, for solve my problem, and i would like to share if you. Its a braking change.

Change QUERYBUILDER.PHP

FROM

__construct(Model $model, Request $request)

TO

__construct(Model $model, array $params = [])

QueryBuilder.php

public function __construct(Model $model, array $params = []) {
                 ...

        $this->uriParser = new UriParser($params);

        ...
}

UriParser.php

public function __construct(array $params = []) {

        $this->uri = (count($params) > 0) ? '?' . http_build_query($params) : NULL;
                ....
}

Example

MODELS/USER


// generic get all items method
public static function listAll(array $params = []) {

        $users = (new QueryBuilder(new User, $params))->build()->paginate();

        return $users;

    }

MODELS/TOURNAMENT

// generic get all items method
public static function listAll(array $params = []) {

        $tournaments = (new QueryBuilder(new Tournament, $params))->build()->paginate();

        return $tournaments;

    }

CONTROLLERS/DASHBOARD

public function recents(Request $request) {

                //now on the same request i can use diferent QueryBuilder instances to filter my collections, based on ARRAY OF PARAMS

        $recentUsers = User::listAll( /*** custom params array here ***/ );
        $recentTournametns = Tournament::listAll( /**** we can even use "$request->all()" to obtain a array of parameters from current request  *** /);

                 return ['users' => $recentUsers, 'tournaments' => $recentTournaments]
    }
selahattinunlu commented 7 years ago

Firstly, thanks for proposal @robsontenorio :) But I did not understand actually why do you need it, sorry. I will be glad if you explain using an example case.

robsontenorio commented 7 years ago

I have my "list all" methods encapsulated in Models. They are encapsulated, because sometimes i can do some custom queries, like: get relations , order by ... not only LIST ALL.

// Games.php - Chess, Mario , Kings ...

public function listAll($request){
     // return api query builder results
}

Other methods in here: save, delete, update ...

// Platforms.php - PS4, XboxOne

public function listAll($request){
     // return api query builder results
}

Other methods in here: save, delete, update ...

// Player.php - usernames ...

public function listAll($request){
     // return api query builder results
}

Other methods in here: save, delete, update ...

So in my endpoint mysite.com/top i would like to show the top records from some entities.

// RecentController.php - list recent records

public function index(Request $request){
     $player = Player::listAll($request);  // recent players: limit=5&order_by=id,desc
     $games = Game::listAll($request);    // recent games added: limit=20&order_by=id,desc
     $plataforms = Game::listAll($request); //recent plataforms added: limit=10&order_by=id,desc

     // return mounted json 
}

In this scenario above my "list all" method have different behavior depending on parameters. By using current implementation ($request object) i cant use custom filters, because all of them depends on the single $request object.

By, using "array of parameters" in QueryBuilder i can build custom queries. It would be an "untied implementation".

public function index(Request $request){

     // using custom params

     $player = Player::listAll(['limit' => 5, ....]);  // recent players: limit=5&order_by=id,desc
     $games = Game::listAll(['limit' => 20, ....]);    // recent games added: limit=20&order_by=id,desc
     $plataforms = Game::listAll(['limit' => 10, ....]); //recent plataforms added: limit=10&order_by=id,desc

     // return mounted json 
}

I'm currently using this approach. I've changed QueryBuilder.phpconstructor and simple changes in related classes.

This "breaking change" allow people to use your lib simple changing $request parameter to $request->all() :

new QueryBuilder(new User(), $request->all()))->build()->paginate();

Or in other custom scenario:

$params = []
new QueryBuilder(new User(), $params))->build()->paginate();
robsontenorio commented 7 years ago

QueryBuilder -> construct

public function __construct(Model $model, array $params = []) {
....
$this->uriParser = new UriParser($params);
....

UriParser -> construct

public function __construct(array $params = []) {
....
$this->uri = (count($params) > 0) ? '?' . http_build_query($params) : NULL;
....
selahattinunlu commented 7 years ago

@robsontenorio I commented to pull request. (https://github.com/selahattinunlu/laravel-api-query-builder/pull/19) Please check it :)

selahattinunlu commented 7 years ago

@ili5 @robsontenorio

I created different solution. You can check it from here https://github.com/selahattinunlu/laravel-api-query-builder/commit/2a85dea4ee09046209d09821725f9b1ceb4b97fa

I will documented it.

I hope you like new solution :)

By the way thanks for idea @ili5 and @robsontenorio

Shortly you can use it like this:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Unlu\Laravel\Api\QueryBuilder;
use Unlu\Laravel\Api\RequestCreator;
use App\User;

class UsersController extends Controller
{
    public function index()
    {
        $request = RequestCreator::createWithParameters([
            'name' => 'selahattin',
            'email' => '!=blabla@example.com'
        ]);

        $qBuilder = new QueryBuilder(new User, $request);

        return response()->json([
            'data' => $qBuilder->build()->get()
        ]);
    }
}