Json response returns nested object - laravel

I am new in vue laravel and trying to get data from API controller with json response. I feel its not in best practices that I get data in nested object. Whats the best way to implement this so that I can get this.user_data.designations = All_Designation(with res.data) and some other like this.user_data.employment = All_Employments, etc.
Also when I try to send data via props I can access it as
designations: this.data.map(d => ({label: d.designation_name, value: d.designation_name, id:d.id}))
I want other data also so I guess it should be like this.data.designations, this.data.employment. This made me confused. how can I manage everything without changing things in trait?
This is my controller method:
public function index()
{
$designations = Designation::all();
if (!$designations->isEmpty()) {
$this->responseData['response'] = 1;
$this->responseData['message'] = "Designations has been Recieved.";
$this->responseData['data'] = $designations;
$this->status = 200;
}
return $this->apiResponse();
}
Api trait:
protected $responseData = [
'response' => 0,
'message' => "Not Found",
'data' => null
];
/**
* #var int
*/
protected $status = 404;
/**
* #return mixed
*/
public function apiResponse()
{
return response()->json($this->responseData, $this->status);
}
Axios Call:
this.$store.dispatch('employee/getDesignations' )
.then(res => { this.user_data.designations = res.data.data })
.catch(err => {
console.error(err)
})

Just return response like this
return response()->json(['response' => ['status' => true, 'data' => $apiResponse]], JsonResponse::HTTP_OK);
HTTP_OK =200 and its a enum type of use Illuminate\Http\JsonResponse;
status =true mean you returning the data .
for example if your data is empty then return something like this
return response()->json(['response' => ['status' => false, 'message' => 'Unable to find data ']], JsonResponse::HTTP_BAD_REQUEST);
just return this .
return response()->json($data, JsonResponse::HTTP_OK);
hope this will hel you

Related

How to return response from async GuzzleHttp request to outer function or handle Exception in yii2?

I need to send async post request in background and save response (status code and request body to DB). I decide to use GuzzleHttp package (v6) for it.
The idea is run function sendAsyncRequest, send async request inside it, then get response from resource in array with keys code, data, return this array to outer function processAsyncRequest and then send it to function logResponse to save it to db.
use GuzzleHttp\Client as GuzzleClient;
class Logger
{
public function processAsyncRequest($client)
{
$response = $this->sendAsyncRequest($client->phone, ['utm_source' => $client->utm_source]);
$this->logResponse($client, $response);
}
public function sendAsyncRequest($phone, $params)
{
$url_params = http_build_query(['utm_source' => $client->utm_source]);
$guzzleClient = new GuzzleClient();
$headers = [
'Content-Type' => 'application/json',
'Authorization' => 'Basic xxxxxxxxxx',
];
$request = new Request('POST', 'url' . $phone . '/tokens/?' . $url_params, $headers);
$promise = $guzzleClient->sendAsync($request);
$promise->then(
function (ResponseInterface $response) {
return [
'code' => $response->getStatusCode(),
'body' => $response->getBody()->__toString(),
];
},
function(RequestException $e) {
return [
'code' => $e->getResponse()->getStatusCode(),
'body' => $e->getMessage(),
];
}
);
$res = $promise->wait();
return $res;
}
public function logResponse($client, $data)
{
$log = new Log();
$log->client_id = $client->id;
$log->url = 'url';
$log->response = $data['code'] . ', ' . $data['body'];
$log->comment = 'reg';
return $log->save();
}
}
The problems are:
function sendAsyncRequest returns object of GuzzleHttp\Psr7\Response, I see the error "Cannot use object of type GuzzleHttp\Psr7\Response as array" and I have no idea how to get my $res array from it.
how to correctly handle exception if promise will return error?

Hiding fields in API Resources Using Gates in Laravel

I have a Product API resource in my application like so
/**
* Transform the resource collection into an array.
*
* #param Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'desc' => $this->desc,
'color' => $this->color,
'amount' => $this->amount,
'available' => $this->available,
'createdAt' => $this->created_at,
'updatedAt' => $this->updated_at,
];
}
I have few roles in my application, like admin, viewer.
When admin access the api, the api returns all fields but when the viewer access the api it returns only limited fields.
How can I handle this using Gates & Policies?
Can I do something like this
'createdAt' => $this->when($this->authorize('product.list'), $this->created_at)
You could use an Eloquent Accessor in your Product model:
public function getCreatedAtAttribute($createdAt)
{
if (Gate::allows('see-product-details', $this)) {
return $createdAt;
} else {
return null;
}
}
Of course you also have to write the see-product-details gate.
Otherwise this may work as well (not tested):
public function getCreatedAtAttribute($createdAt)
{
if ($this->authorize('view', [Product::class, $this])) {
return $createdAt;
} else {
return null;
}
}

Laravel 5 - Allow to pass only FormRequest rules fields

I would like to use FormRequest validation in which
allow request fields only in rules() return array keys.
In the below code, I would like to allow request fields only 'os', 'number', 'version'. If request include the other field , return error response.
How can I modify the code ?
public function rules()
{
return [
'os' => [
'required',
\Rule::in(['android', 'ios']),
],
'number' => 'required|integer',
'version' => ['required', 'regex:/^\d+.\d+.\d+$/'],
];
}
There is a way you can do this using form request. It may not send the proper response but it works.
In your Form Request's authorize method use the following code.
public function authorize ()
{
$params = $this->request->keys();
$os_status = in_array('os', $params);
$number_status = in_array('number', $params);
$version_status = in_array('version', $params);
$check = $os_status & $number_status & $version_status & (count($params) != 3 ? false : true);
return $check;
}
It will return HTTP response with 403 status code.

laravel on saving model return json from validation

Hi I'm having a problem outputting my json information on saving method in the model. I get the following error -
UnexpectedValueException in Response.php line 397:
The Response content must be a string or object implementing __toString(), "boolean" given.
I do validation on the model while saving and in the validate method of the model I need to out put the json but I'm getting boolean instead of json object
Javascript:
submit: function(e) {
e.preventDefault();
var contact = this.model.save({
firstname: this.firstname.val(),
lastname: this.lastname.val(),
company: this.company.val(),
email_address: this.email_address.val(),
description: this.description.val(),
}, {success:function(response){ console.log(response)}, wait: true});
Contact Model:
class Contact extends Model
{
protected $table = "contacts";
protected $fillable = ['firstname', 'lastname', 'company', 'email_address', 'description'];
public static function boot() {
parent::boot();
static::creating(function($model) {
return $model->validate('POST');
});
static::updating(function($model) {
return $model->validate('PUT');
});
static::saving(function($model) {
return $model->validate('PUT');
});
}
public function rules($method)
{
switch($method)
{
case 'GET':
case 'DELETE':
{
return [];
}
case 'POST':
{
return [
'firstname' => 'required',
'lastname' => 'required',
'email_address' => 'required|email|unique:contacts,email_address',
'description' => 'requried'
];
}
case 'PUT':
case 'PATCH':
{
return [
'firstname' => 'required',
'lastname' => 'required',
'email_address' => 'required|email|unique:contacts,email_address,'.$this->id,
'description' => 'required',
];
}
default: break;
}
return [];
}
public function messages() {
return [
'firstname.required' => 'Please enter your first name.',
'lastname.required' => 'Please enter your first name.',
'email_address.required' => 'Please enter a email address.',
'email_address.email' => 'Please enter a valid email address',
'email_address.unique' => 'The email is not unique.',
'description' => 'Please enter a description.'
];
}
public function validate($method)
{
$data = $this->attributes;
// if( $data['slug'] === '') {
// // if the slug is blank, create one from title data
// $data['slug'] = str_slug( $data['title'], '-' );
// }
// make a new validator object
$v = Validator::make($data, $this->rules($method), $this->messages());
// check for failure
if ($v->fails())
{
// set errors and return false
// json here not return response it's always boolean true or false
return new JsonResponse(array('error' => true, 'errors' => $v->messages()));
}
// validation pass
return true; //new JsonResponse(array('errors'=>false));
}
public function errors() {
return $this->errors;
}
public function user() {
return $this->hasOne('App\User', 'email', 'email_address');
}
}
Saving the model:
public function update(Request $request, $id) {
$contact = Contact::find($id)->with('user')->first();
$contact->firstname = $request->get('firstname');
$contact->lastname = $request->get('lastname');
$contact->email_address = $request->get('email_address');
$contact->company = $request->get('company');
$contact->description = $request->get('description');
return $contact->save(); //return formatted json
}
According to your implementation of validation, you should change the following part (in Contact):
// check for failure
if ($v->fails())
{
// set errors and return false
// json here not return response it's always boolean true or false
return new JsonResponse(array('error' => true, 'errors' => $v->messages()));
}
To something like this:
if ($v->fails()) {
$this->errors = $v->errors();
return false;
}
Then, from the Controller, try something like this:
// If validation failed
if(!$contact->save()) {
return response()->json([
'error' => true,
'errors' => $contact->errors()
]);
}
// Contact created if reached here...
return response()->json(['error' => false, 'contact' => $contact]);
Also, check the Ajax-Request-Validation and Form-Request-Validation (Easier and Managable).
Note: Don't try to return any kind of HTTP Response from model. Returning the HTTP response is part of your application logic and model should not care about these.
As save() does return boolean so You've to check if it's ok.
1) Change Your Contact model to put errors to model's errors param:
/* if($v->fails()) remove/comment this line
...
} */
$this->errors = $v->errors();
return !$v->fails();
2) In Your controller put this code:
public function update(Request $request, $id) {
$contact = Contact::find($id)->with('user')->first();
if(!$contact) {
return response('Contact not found', 404);
}
$contact->firstname = $request->get('firstname');
$contact->lastname = $request->get('lastname');
$contact->email_address = $request->get('email_address');
$contact->company = $request->get('company');
$contact->description = $request->get('description');
return $contact->save()?
$contact->toJson() : // returns 200 OK status with contact (json)
response($contact->errors, 400); // returns proper 400 Bad Request header with errors (json) in it
}
p.s. it's nice to answer to requester with http status, industry has made all to make life of developer easy, so if it's not 2xx, 3xx status so => response for Your request from client-side app will be treated as error (handler success: function(response) will not catch error here)

How I do do get session in model? CakePHP 3.x

Cakephp 3.x
I want to do my captcha custom validation. But I can not access a session.
$validator->notEmpty('securityCode', __('not empty message'))
->add('securityCode','custom',
['rule' => function ($value, $context) use ($extra) {
if($this->request->Session()->read('captcha') != $value) {
return false;
}
return true;
}, 'message' => 'error security code']);
return $validator;
or can I my custom validation function give custom parameter?
public function validationLogin(Validator $validator, $customParameter)
{ //bla bla }
I use: http://book.cakephp.org/3.0/en/core-libraries/validation.html#custom-validation-rules
You can pass Session data as parameter of validation function like this
// In Controller
$sessionData = $this->request->Session()->read('captcha');
$validator = $this->{YourModel}->validationLogin(new Validator(), $sessionData);
$errors = $validator->errors($this->request->data());
if (!empty($errors)) {
// Captcha validation failed
}
// In Model
public function validationLogin(Validator $validator, $sessionData)
{
$validator
->notEmpty('securityCode', __('not empty message'))
->add('securityCode', 'custom', [
'rule' => function ($value, $context) use ($sessionData) {
if ($sessionData != $value){
return false;
}
return true;
},
'message' => 'error securty code'
]);
return $validator;
}
Edit: you can access session from model, but it is not a good practise and you better avoid it. Instead rather pass it from controller as in example above
// In model
use Cake\Network\Session;
$session = new Session();
$sessionData = $session->read('captcha');
For CakePHP 3: at the top of your Model class add
use Cake\Network\Session;
and at the point where you want to have to access the session add
$this->session = new Session();
$messages = $this->session->read('captcha'); // Example for the default flash messages
To set a flash message in the model use
$this->session = new Session();
$messages = $this->session->read('Flash.flash');
$messages[] = ['message' => 'YOUR FLASH MESSAGE', 'key' => 'flash', 'element' => 'Flash/default', 'params' => []];
$this->session->write('Flash.flash', $messages);

Resources