How to pass custom request to Api Resource - php

I have custom Request class:
class ApiRequest extends FormRequest
{
...
public function locale()
{
$lang = $this->header('Accept-Language');
return $lang ? $lang : 'uz';
}
}
and API recourse:
class SomeResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
...
];
}
}
but I can't use ApiRequest's method locale() in SomeResource. Because toArray() accepts only \Illuminate\Http\Request.
Is there any idea that I pass my class into this? Or a better solution from you.

The request instance bound in the container is injected into the JSON resource class when the response is created.
One method to achieve what you want would be to create your custom request that extends the standard request class and then bind that in the container as the request instance to be used for the remainder of the request. This could be done in a middleware assigned specifically to the API group or just in specific controllers.
This will then be injected into the toArray() method in your JSON resource class when the response is built and your custom methods will be available to use.
Custom Request Class
class ApiRequest extends Request
{
...
public function locale()
{
$lang = $this->header('Accept-Language');
return $lang ? $lang : 'uz';
}
}
API Middleware
class ApiMiddleware
{
...
public function handle($request, $next)
{
$request = app()->instance('request', ApiRequest::createFrom($request));
return $next($request);
}
}
JSON Resource
class SomeResource extends JsonResource
{
public function toArray($request)
{
// $request is now instanceof ApiRequest
return [
'id' => $this->id,
'locale' => $request->locale()
];
}
}

API Resources should receive model instances not Requests itself. This is an example:
ApiRequest.php
ApiRequest extends Request {
public function authorize() { /** your logic */ }
public function rules() { /** your logic */ }
}
MyCoolController.php
MyCoolController extends Controller {
public function myFunction(ApiRequest $request)
{
$lang = $request->header('Accept-Language') ?? 'uz';
\App::setLocale($lang); // <--
$model = MyModel::find($request->get('id'));
return new SomeResource($model);
}
}

I created a base Resource class, that all my other resource classes extend it with my custom method:
class BaseResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
protected function locale($request)
{
$lang = $request->header('Accept-Language');
return $lang ? $lang : 'uz';
}
}
and I use it in child class:
class SomeResource extends BaseResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
$lang = $this->locale($request);
return [
'id' => $this->id,
'hash' => $this->hash,
'name' => $this->translation('name', $this->locale($request)),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}

Related

Undefined property: Illuminate\Database\Query\Builder::$sessionId

This is my PlayerController, Player & Session Model and Resource.
I want to use the input (sessionId from SessionsTable) to fetch user from the room with the same id (userSession) and return an array in this format: [{userId:1, userName: stacki, userVote:8},{...},...]
I already asked [here][1] to achieve this and now im stuck with this error.
What do I have to change in order to solve this issue? Simply adding ->first() does not solve my issue, I need more than one record.
namespace App\Http\Controllers;
use App\Player;
use Illuminate\Http\Request;
use App\Http\Resources\Players as PlayerResource;
class PlayerController extends Controller
{
public function index(Request $request)
{
$room = $request->input('sessionId');
$currentPlayers = Player::where('userSession', $room)->get();
return PlayerResource::collection($currentPlayers);
}
public function create()
{ }
public function update()
{ }
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Player extends Model
{
protected $fillable = [];
public $sortable = [
'userId',
'userName',
'userVote'
];
public function sessions()
{
return $this->hasMany('App\Session');
}
public function players(){
return $this->belongsToMany('App\Session');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Session extends Model
{
protected $fillable = [];
public function user(){
return $this->belongsToMany('App\Player');
}
public function creator()
{
return $this->hasOne('App\Player', 'userId');
}
}
class Players extends ResourceCollection
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'userId' => $this->sessionId,
'userName' => $this->userName,
'userVote' => $this->userVote
];
}
}
`
[1]: https://stackoverflow.com/questions/58062014/display-db-entries-in-json-array-in-controller-laravel-php
Your Player class might extends the Illuminate\Http\Resources\Json\JsonResource instead of ResourceCollection.
This should solve your problem.
use Illuminate\Http\Resources\Json\JsonResource;
class Players extends JsonResource
{
/**
* Transform the resource collection into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'userId' => $this->sessionId,
'userName' => $this->userName,
'userVote' => $this->userVote
];
}
}
Hope it helps.

Pass id parameter to resource in Laravel

I have the following method in my Laravel controller:
public function specialOffers($id) {
return \App\Http\Resources\SpecialOfferResource::collection(Offers::all());
}
I need some special manipulations, so I've created this SpecialOfferResource resource. The resource code is:
class SpecialOfferResource extends Resource {
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request) {
//here I need the $id passed to the controller's method,
//but I only have $request
return [
//my request fields, everything ok
];
}
}
How can I pass $id from the controller's method to this resource? I know I can pass through the request as a field, but is it possible this other way?
The resource collection is just a wrapper that formats, or maps, the collection you pass to it.
The collection you are passing is Offers::all(), which would include all Offers models.
You'd likely want to use the query builder to narrow down the collection you are passing:
public function specialOffers($id) {
$results = Offers::where('column', $id)->get();
return \App\Http\Resources\SpecialOfferResource::collection($results);
}
I'm not sure whether this is acceptable or not, but in some case i do need some parameter passed from controller to use inside toArray resource method and this is what i did.
Create resource class that extend Illuminate\Http\Resources\Json\ResourceCollection.
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TestResource extends ResourceCollection
{
private $id;
public function __construct($id, $collection)
{
parent::__construct($collection);
$this->id = $id;
}
public function toArray($request)
{
return [
'data' => $this->collection,
'id' => $this->id
];
}
}
And from controller you can call like this:
<?php
namespace App\Http\Controllers;
use App\Http\Resources\TestResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class TestController extends Controller
{
public function index()
{
$id = 30;
$collection = collect([['name' => 'Norli'], ['name' => 'Hazmey']]);
return new TestResource($id, $collection);
}
}

Laravel 5.3 dynamically binding validator to custom form request

I'm developing in a system using Laravel 5.3 and I"m trying to update custom validation rules. The architecture is currently as follows:
ProfileStoreRequest --> ProfileValidator
ClientStoreRequest --> ClientValidator
...
What I'm basically trying to do here is to have only one object named "StoreRequest" which will call the correct validator depending on the route which is being called. Here's what I have so far:
In my routes:
Route::group([
'prefix' => 'manage',
'namespace' => 'Manage',
'validators' => [
'manage.profile.storeAjax' => [
'name' => "required|max:40",
'color' => "integer|digits_between:0,7",
'service' => "integer", //digits_between:3,10
'company_id' => "required|integer|exists:companies,id,deleted_at,NULL",
'site_id' => "integer|siteIdExists"
]
]], function () {
Route::post('/site/storeAjax', 'SiteController#storeAjax')->name('manage.site.storeAjax');
Route::post('/company/storeAjax', 'CompanyController#storeAjax')->name('manage.company.storeAjax');
Route::post('/employee/store', 'EmployeeController#store')->name('manage.employee.store');
Route::post('/employee/addProfile', 'EmployeeController#addProfile')->name('manage.employee.addProfile');
Route::post('/employee/removeProfile', 'EmployeeController#removeProfile')->name('manage.employee.removeProfile');
Route::post('/employee/addSite', 'EmployeeController#addSite')->name('manage.employee.addSite');
Route::post('/employee/removeSite', 'EmployeeController#removeSite')->name('manage.employee.removeSite');
Route::post('/message/storeAjax', 'MessageController#storeAjax')->name('manage.message.storeAjax');
Route::post('/profile/storeAjax', 'ProfileController#storeAjax')->name('manage.profile.storeAjax');
Route::post('/timeEntry/storeAjax', 'TimeEntryController#storeAjax')->name('manage.timeEntry.storeAjax');
});
Next is my StoreRequest:
namespace App\Http\Requests;
use App\Http\Validators\ProfileValidator;
use Auth;
//use App\Model\TimeEntry;
use DateUtil;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
class StoreRequest extends AbstractRequest {
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize() {
// prj(__METHOD__);
$authorized = parent::authorize();
if ($authorized) {
$user = Auth::user();
if ($user && $user->can('write')) {
return true;
} else {
return false;
}
}
return false;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules() {
parent::rules();
return $this->route()->getAction()['validators'][$this->route()->getName()];
}
/**
* User messages
*
* #return array
*/
public function messages() {
$messages = array_merge(parent::messages(), [
'exists' => 'The selected :attribute is invalid for this time entry id.'
]);
return $messages;
}
public function validate()
{
parent::validate();
}
}
And of course, all of my custom validators are registered using service providers (here's an example with a profileValidator):
profileServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Validator;
class ProfileServiceProvider extends ServiceProvider {
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot() {
Validator::extend('siteIdExists', 'App\Http\Validators\ProfileValidator#validateSiteIdExists');
}
/**
* Register any application services.
*
* #return void
*/
public function register() {
//
}
}
ProfileValidator.php
namespace App\Http\Validators;
use App\Model\Site;
use Mockery\Exception;
class ProfileValidator
{
public function validateSiteIdExists($attribute, $value, $parameters, $validator)
{
if ($value == -1)
return true;
else
{
return Site::where(
[
['id', '=', $value],
['company_id', '=', $validator->getData()['company_id']]
]
)->whereNull('deleted_at')->exists();
}
}
}
So basically, my StoreRequest is capable of loading its validation rules from the route... however, no matter how hard I try, I can't figure how to bind the validator I want. Can anyone help me?

Filter data in form request in Laravel 5.5

I have generated new form Request for the controller, but I do not know how to filter data before there will handle in the validator and so on.
Are there some native solutions in Laravel for this case?
class TestRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
"title" => "required|string",
"order" => "required|integer"
];
}
}
class TestController extends Controller
{
public function store(TestRequest $request, $chapterId)
{
// some business logic
}
}
There is some solution in Laravel 5.5 but in this example author uses
validate for filtering data from request, but I need to use filter inside TestRequest
$data = $this->validate(request(), [
//...
]); // I can't use this inside TestRequest
You can use my package: https://github.com/mikicaivosevic/laravel-filters
It's allows you to filter request values before validation...
<?php
class LoginRequest extends FormRequest {
//Filter
public function filters()
{
return [
'name' => 'lower',
'id' => 'int',
];
}
//...
}
Convert $request->name value into lowercase.
Conert $request->id value into integer.

Laravel 5.4: Class 'App\Http\Controllers\Response' not found error

I am following the Laracast's API tutorial and trying to create an ApiController that all the other controllers extend. ApiController is responsible for response handling.
class ApiController extends Controller
{
protected $statusCode;
public function getStatusCode()
{
return $this->statusCode;
}
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
}
public function respondNotFound($message = 'Not Found!')
{
return Reponse::json([
'error' => [
'message' => $message,
'status_code' => $this->getStatusCode()
]
]);
}
}
And i also have a ReportController that extends ApiController.
class ReportController extends ApiController
{
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$report = Report::find($id);
if (! $report ) {
$this->respondNotFound(Report does not exist.');
}
return Response::json([
'data'=> $this->ReportTransformer->transform($report)
], 200);
}
}
When i try to call respondNotFound method from ReportController i get
Class 'App\Http\Controllers\Response' not found error
eventhough i add use Illuminate\Support\Facades\Response;to parent or child class i get the error. How can i fix this ?
Any help would be appreciated.
Since it's a facade, add this:
use Response;
Or use full namespace:
return \Response::json(...);
Or just use helper:
return response()->json(...);

Categories