Customize error message in yii2 REST API - api

I want to customize this following error message
Error 404: Not Found
{
"name": "Not Found",
"message": "Object not found: 6",
"code": 0,
"status": 404,
"type": "yii\\web\\NotFoundHttpException"
}
to:
Error 404: Not Found
{
"name": "Not Found",
"message": "Country not found for: 6",
"code": 404,
"status": Error
}
Where is need to write this customization code?

try this (e.g. config/web.php):
return [
// ...
'components' => [
// ...
'response' => [
// ...
'on beforeSend' => function (yii\base\Event $event) {
$response = $event->sender;
if (404 === $response->statusCode && is_array($response->data)) {
$response->data['code'] = $response->data['status'];
$response->data['status'] = 'Error';
unset($response->data['type']);
}
},
],

You need to override errorHandler which extends yii\web\ErrorHandler, just convertExceptionToArray method
/**
* Converts an exception into an array.
* #param \Exception|\Error $exception the exception being converted
* #return array the array representation of the exception.
*/
protected function convertExceptionToArray($exception)
{
if (!YII_DEBUG && !$exception instanceof UserException && !$exception instanceof HttpException) {
$exception = new HttpException(500, Yii::t('yii', 'An internal server error occurred.'));
}
$array = [
'name' => ($exception instanceof Exception || $exception instanceof ErrorException) ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
];
if ($exception instanceof HttpException) {
$array['status'] = $exception->statusCode;
}
if (YII_DEBUG) {
$array['type'] = get_class($exception);
if (!$exception instanceof UserException) {
$array['file'] = $exception->getFile();
$array['line'] = $exception->getLine();
$array['stack-trace'] = explode("\n", $exception->getTraceAsString());
if ($exception instanceof \yii\db\Exception) {
$array['error-info'] = $exception->errorInfo;
}
}
}
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->convertExceptionToArray($prev);
}
return $array;
}
The code above is from Yii, you just need to tweak it a little.
Then add it to config : ( usually web.php)
'errorHandler' => [
'class' => 'your\namespace\YourErrHandler',
],

Im not sure what I do is right or wrong but it works.
Just create a new custom php file called ErrorMsg.php
<?php
use Yii;
use yii\web\HttpException;
class ErrorMsg extends \Exception
{
public static function customErrorMsg($error_code,$message = null, $code = 0, \Exception $previous = null,$extra_content=NULL)
{
$httpException = new HttpException($error_code,$message,$code,$previous);
Yii::$app->response->statusCode = $error_code;
$custom_err = array(
'name'=> $httpException->getName(),
'message' => $message,
'code' => $code,
'extraContent' => $content,
'status' => $error_code
);
return $custom_err;
}
and call the functions wherever you want. Example
return ErrorMsg::customErrorMsg(400,"Message Here",11,NULL,"Extra Content Here");

Related

TypeError: response.data is undefined

I'm having problems with promise response for a vForm PUT to UPDATE a model (backend in laravel).
The response code is 200 (OK, updated) and the model is updated, but I don't know why I'm having error with "response.data" in catch. There is no error and code in ".then()" is running correctly.
EDIT
Service Update funciton (vue) using vForm.
updateService(){
this.$Progress.start();
this.service.put('api/service/' + this.service.id)
.then( function (response) {
Toast.fire({
type: 'success',
title: response.data['Response']
});
this.$Progress.finish();
})
.catch( function (response) {
console.log(response);
Swal.fire("Error!", response.data['Response'], "warning");
this.$Progress.fail();
});
this.$events.$emit('ServiceInform');
},
Function in backend (laravel).
public function update(Request $request, Service $service)
{
$this->validate($request, [
'id_customers' => 'required|int',
'date' => 'required|date',
'id_technicians' => 'required|int',
'location' => 'required|string',
'details' => 'required|string'
]);
if ($request['id_technicians'] !== $service['id_technicians']) {
$assignated_by = Auth::user()->id;
$assigned_date = date('Y-m-d H:i:s');
} else {
$assignated_by = $service['assignated_by'];
$assigned_date = $service['assigned_date'];
}
if ($request['id_technicians'] == 0) {
$state = 'P';
} else {
$state = 'I';
}
$service->date = $request['date'];
$service->id_technicians = $request['id_technicians'];
$service->location = $request['location'];
$service->details = $request['details'];
$service->assigned_date = $assigned_date;
$service->assigned_by = $assignated_by;
$service->state = $state;
try {
$service->save();
return Response::json([
'Response' => 'Servicio actualizado.'
], 201);
} catch (Exception $e) {
return Response::json([
'Response' => 'No se actualizó el servicio.'
], 422);
}
}
This line looks problematic to me:
this.$Progress.finish();
It's trying to access this within the function passed to then. It seems unlikely that this will be referencing what you're expecting. You should be able to confirm with suitable console logging. My suspicion is that attempting to call this.$Progress.finish() will throw an error, triggering the catch.
Try using arrow functions for your then and catch callbacks instead.

Errors while debugging code using HTML::TreeBuilder::XPath

I am getting some errors when I try to debug the following code.
Note that it fetches the data from approx 6,000 fields from the http://europa.eu/youth/volunteering/evs-organisation#open
After parsing each page, check for the existence of the next › link at the bottom.
View-source is a browser based command. It tells the browser to output the response in plain text rather than render it based on its actual content type, HTML in this case. You should not need to include view-source in your URL.
Here we have a script that extracts the data out of each block and cleans it up a little. The browse function is generic. It takes an input reference which contains the URL and XPaths of the parent and children in order to construct the output ref. It is just an approach: it does not yet navigate across each page,
In a rough script I tested, I fetched the total results using //span[#class="ey_badge"] then the max page using
my $page_max = $results / 21;
$page_max = int( $page_max ) == $page_max ? $page_max-- : int( $page_max ) ;
See the errors
martin#linux-3645:~/dev/perl> perl eu.pl
syntax error at eu.pl line 81, near "our "
Global symbol "$iterator_organizations" requires explicit package name at eu.pl line 81.
Can't use global #_ in "my" at eu.pl line 84, near "= #_"
Missing right curly or square bracket at eu.pl line 197, at end of line
Execution of eu.pl aborted due to compilation errors.
martin#linux-3645:~/dev/perl> ^C
martin#linux-3645:~/dev/perl>
It fetches the data from approx 6,000 fields from http://europa.eu/youth/volunteering/evs-organisation#open
See the code
use strict;
use warnings FATAL => qw#all#;
use LWP::UserAgent;
use HTML::TreeBuilder::XPath;
use Data::Dumper;
my $handler_relurl = sub { q#https://europa.eu# . $_[0] };
my $handler_trim = sub { $_[0] =~ s#^\s*(.+?)\s*$#$1#r };
my $handler_val = sub { $_[0] =~ s#^[^:]+:\s*##r };
my $handler_split = sub { [ split $_[0], $_[1] ] };
my $handler_split_colon = sub { $handler_split->( qr#; #, $_[0] ) };
my $handler_split_comma = sub { $handler_split->( qr#, #, $_[0] ) };
my $conf = {
url => q#https://europa.eu/youth/volunteering/evs-organisation_en#,
parent => q#//div[#class="vp ey_block block-is-flex"]#,
children => {
internal_url => [ q#//a/#href#, [ $handler_relurl ] ],
external_url => [ q#//i[#class="fa fa-external-link fa-lg"]/parent::p//a/#href#, [ $handler_trim ] ],
title => [ q#//h4# ],
topics => [ q#//div[#class="org_cord"]#, [ $handler_val, $handler_split_colon ] ],
location => [ q#//i[#class="fa fa-location-arrow fa-lg"]/parent::p#, [ $handler_trim ] ],
hand => [ q#//i[#class="fa fa-hand-o-right fa-lg"]/parent::p#, [ $handler_trim, $handler_split_comma ] ],
pic_number => [ q#//p[contains(.,'PIC no')]#, [ $handler_val ] ],
}
};
print Dumper browse( $conf );
sub browse {
my $conf = shift;
my $ref = [ ];
my $lwp_useragent = LWP::UserAgent->new( agent => q#IE 6#, timeout => 10 );
my $response = $lwp_useragent->get( $conf->{url} );
die $response->status_line unless $response->is_success;
my $content = $response->decoded_content;
my $html_treebuilder_xpath = HTML::TreeBuilder::XPath->new_from_content( $content );
my #nodes = $html_treebuilder_xpath->findnodes( $conf->{parent} );
for my $node ( #nodes ) {
push #$ref, { };
while ( my ( $key, $val ) = each %{ $conf->{children} } ) {
my $xpath = $val->[0];
my $handlers = $val->[1] // [ ];
$val = ( $node->findvalues( qq#.$xpath# ) )[0] // next;
$val = $_->( $val ) for #$handlers;
$ref->[-1]->{$key} = $val;
}
}
return $ref;
}
{
'internal_url' => 'https://europa.eu/youth/volunteering/organisation/948417016_en',
'external_url' => 'http://www.apd.ge',
'location' => 'Tbilisi, Georgia',
'title' => '"Academy for Peace and Development" Union',
'topics' => [
'Access for disadvantaged',
'Youth (Participation, Youth Work, Youth Policy)',
'Intercultural/intergenerational education and (lifelong)learning'
],
'pic_number' => '948417016',
'hand' => [
'Receiving',
'Sending'
]
}
our $iterator_organizations = sub {
my ( $browser, $parent ) = #_;
my $url = q#https://europa.eu/youth/volunteering/evs-organisation_en#;
my $nodes = $browser->nodes( url => $url );
my $iterator = sub {
return shift #$nodes;
};
return ( $iterator, 1 );
our $iterator_organizations_b = sub {
my ( $browser, $parent ) = #_;
my $url = q#https://europa.eu/youth/volunteering/evs-organisation_en#;
my $uri = URI->new( $url );
my $xpath = q#//div[#class="vp ey_block block-is-flex"]#;
my $nodes = [ ];
my $page = 0;
my $results = $parent->{results};
my $page_max = $results / 21;
$page_max = int($page_max) == $page_max ? $page_max-- : int($page_max);
my $iterator_uri = sub {
$uri->query_form( page => $page++ );
return $page > 2 ? undef : $uri ; # $page_max;
};
my $iterator_node = sub {
unless ( #$nodes ) {
my $uri = $iterator_uri->( ) // return undef;
my $options = $page == 1 ? { tree => $parent->{_node} } : { url => $uri->as_string };
$nodes = $browser->nodes( %$options, xpath => $xpath );
}
return shift #$nodes;
};
return ( $iterator_node, 0 );
};
our $iterator_organization = sub {
my ( $browser, $parent ) = #_;
my $url = $parent->{internal_url};
my $nodes = $browser->nodes( url => $url );
my $iterator = sub {
return shift #$nodes;
};
return ( $iterator, 1 );
};
sub organizations {
my ( $self, $options ) = ( shift, { #_ } );
my $map = [
$Massweb::Browser::Europa::iterator_organizations,
results => q#.//span[#class="ey_badge"]#,
organizations => [
$Massweb::Browser::Europa::iterator_organizations_b,
internal_url => [ q#.//a/#href#, $Massweb::Browser::Europa::handler_url ],
external_url => [ q#.//i[#class="fa fa-external-link fa-lg"]/parent::p//a/#href#, $Massweb::Browser::handler_trim ],
title => q#.//h4#,
topics => [ q#.//div[#class="org_cord"]#, $Massweb::Browser::handler_val, $Massweb::Browser::handler_list_colon ],
location => [ q#.//i[#class="fa fa-location-arrow fa-lg"]/parent::p#, $Massweb::Browser::handler_trim ],
hand => [ q#.//i[#class="fa fa-hand-o-right fa-lg"]/parent::p#, $Massweb::Browser::handler_trim, $Massweb::Browser::handler_list_comma ],
pic_number => [ q#.//p[contains(.,'PIC no')]#, $Massweb::Browser::handler_val ],
recruiting => [ q#boolean(.//i[#class="fa fa-user-times fa-lg"])#, $Massweb::Browser::handler_bool_rev ],
_ => \&organization,
],
];
my $organizations = $self->browse( map => $map );
return $organizations;
}
sub organization {
my ( $self, $options ) = ( shift, { #_ } );
my $map = [
sub { $Massweb::Browser::Europa::iterator_organization->( $_[0], $options ) },
#title => q#.//h1#,
description => q#.//div[#class="ey_vp_detail_page"]/p#,
];
my $organization = $self->browse( map => $map );
return $organization;
}
The problem appears to be the block/anonymous hash starting 'internal_url'. I can't imagine what you intend there but it is a syntax error and would have no effect if you fixed it
Why are you declaring so many subroutine references like our $iterator_organizations = sub { ... } instead of using standard subroutines? It is a very strange approach

Yii2-How to access a variable from model to a controller?

I am working on yii2. I have came across a point in which I have to send an email to a person when a meter is installed and it's images are uploaded to the server. Fro this I have already configured the swift mailer.
There is a model named Installations which have a function which saves all the installation data.
public static function saveAll($inputs){
$coutner = 0;
$arr_status = [];
foreach ($inputs as $input) {
$s = new Installations;
foreach ((array)$input as $key => $value) {
if($key != 'image_names') {
if ($s->hasAttribute($key)) {
$s->$key = $value;
}
}
}
$user = Yii::$app->user;
if (isset($input->auth_key) && Users::find()->where(['auth_key' => $input->auth_key])->exists()) {
$user = Users::find()->where(['auth_key' => $input->auth_key])->one();
}
$s->created_by = $user->id;
if (Installations::find()->where(['ref_no' => $input->ref_no])->exists()) {
$arr_status[] = ['install_id' => $input->install_id, 'status' => 2, 'messages' => "Ref # Already exists"];
continue;
}
$s->sync_date = date('Y-m-d H:i:s  ');
if($s->save()){
if ($s->istallation_status == 'Installed') {
Meters::change_status_byinstall($s->meter_msn, Meters::$status_titles[4]);
}
else if ($s->istallation_status != 'Installed' && $s->comm_status =='Failed')
{
Meters::change_status_byinstall($s->meter_msn, Meters::$status_titles[5]);
}
$arr_status[] = ['install_id' => $input->install_id, 'status' => 1];
$coutner++;
if (isset($input->doc_images_name)) {
foreach ($input->doc_images_name as $img) {
$image = new InstallationImages;
$image->image_name = $img->image_name;
$image->installation_id = $s->id;
$image->save();
}
}
if (isset($input->site_images_name)) {
foreach ($input->site_images_name as $img2) {
$image2 = new InstallationImagesSite;
$image2->image_name = $img2->image_name;
$image2->installation_id = $s->id;
$image2->save();
}
}
}else{
$arr_status[] = ['install_id' => $input->install_id, 'status' => 0, 'messages' => $s->errors];
}
$status = $s->istallation_status;
$msn = $s->meter_msn;
$com = $s->comm_status;
// want to pass these variables to the controller function
}
return ['status' => 'OK', 'details' => $arr_status, 'records_saved' => $coutner];
}
Now There Is a Controller name InstallationController. This controller contains all the APIs for my mobile application. Below are two main functions in it
public function actionAddnew()
{
$fp = fopen('debugeeeeeee.txt', 'w+');
fwrite($fp, file_get_contents('php://input'));
fclose($fp);
$inputs = json_decode(file_get_contents('php://input'));
return Installations::saveAll($inputs);
}
public function actionSavephoto()
{
try {
$count = 0;
foreach ($_FILES as $f) {
$dd = pathinfo($f['name']);
if (!isset($dd['extension']) || !in_array($dd['extension'], array('jpg', 'png', 'gif'))) {
return ['status' => 'ERROR', 'uploaded_files' => $count, 'message' => 'Invalid File'];
break;
}
if (move_uploaded_file($f['tmp_name'], Installations::UPLOAD_FOLDER . $f['name'])) {
$count++;
return ['status' => 'OK', 'uploaded_files' => $count];
break;
} else {
return ['status' => 'ERROR', 'uploaded_files' => $count];
break;
}
}
} catch (Exception $x) {
return ['status' => 'ERROR', 'message' => $x->getMessage()];
}
}
The mobile application will call the Addnew() api and after that it will call the savephoto. Now I want to pass $msn,$status and $com values from the Model to the controller function Savephoto.
For this I have tried to use session variables but still I am unable to get by desired result(s).
I have also checked the question Yii, how to pass variables to model from controller?
but it didn't worked for me.
How can I achieve it?
Any help would be highly appreciated.
The only way to get those values out of saveAll() is to return them. Presently, they are defined on an object in $s that is overwritten each loop. The best way to do that seems to be creating an array outside of your foreach ($inputs... loop and appending each created Installations object.
Return that at the end, and pass it (or just the relevant element from it) into actionSavephoto() as a parameter. Then, those values will be accessible of properties of that passed object. This handling will occur in the code that is not pictured which calls actionAddNew() and then actionSavephoto()

React Native setItem in storage

I have an forEach loop as follows:
let result_test = [];
forEach(result_to_upload, value => {
if (value.picturepath) {
let body = new FormData();
const photo = {
uri: value.picturepath,
type: 'image/jpeg',
name: value.pictureguid + '.jpg',
};
body.append('image', photo);
let xhr = new XMLHttpRequest();
xhr.open('POST', data_url + "/manager/transport/sync/picture/?pictureguid=" + value.pictureguid);
xhr.onload = (e) => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
result_test.push(
{
"vehicle_id": value.vehicle_id,
"slot_id": value.slot_id,
"area": value.area,
"zone": value.zone,
"aisle": value.aisle,
"side": value.side,
"col": value.col,
"level": value.level,
"position": value.position,
"timestamp": value.timestamp,
"picturepath": value.picturepath,
"pictureguid": value.pictureguid,
"reason": value.reason,
"handled": value.handled,
"uploaded": 1
}
);
}
}
};
xhr.onerror = (e) => console.log('Error');
xhr.send(body);
} else {
result_test.push(
{
"vehicle_id": value.vehicle_id,
"slot_id": value.slot_id,
"area": value.area,
"zone": value.zone,
"aisle": value.aisle,
"side": value.side,
"col": value.col,
"level": value.level,
"position": value.position,
"timestamp": value.timestamp,
"picturepath": value.picturepath,
"pictureguid": value.pictureguid,
"reason": value.reason,
"handled": value.handled,
"uploaded": 1
}
)
}
});
AsyncStorage.setItem('vehicle_slot', JSON.stringify(result_test), () => s_cb())
And result to upload is as follows:
[
{
aisle:""
area:""
category_text: "CT"
col:2
color_text:"Argent"
comment:""
handled:0
level:0
make_text:"Peugeot"
model_text:"208"
pictureguid:"88e6a87b-b48b-4bfd-b42d-92964a34bef6"
picturepath:
"/Users/boris/Library/Developer/CoreSimulator/Devices/E5DB7769-6D3B-4B02-AA8F-CAF1B03AFCB7/data/Containers/Data/Application/DBCFB503-F8E1-42FF-8C2B-260A713AF7BC/Documents/2D840EFA-014C-48C0-8122-53D9A0F4A88E.jpg"
position:0
reason:"ENTER"
reference:""
registration:""
side:"E"
slot_id:2358
tag_text:""
timestamp:"201705021714"
uploaded:0
vehicle_id:1
vin:"123456"
zone:"A"
},
{
aisle:""
area:""
category_text: "CT"
col:2
color_text:"Argent"
comment:""
handled:0
level:0
make_text:"Golf"
model_text:"208"
pictureguid:"88e6a87b-b48b-4bfd-b42d-92964a34bef6"
picturepath:""
position:0
reason:"ENTER"
reference:""
registration:""
side:"B"
slot_id:2358
tag_text:""
timestamp:"201705021714"
uploaded:0
vehicle_id:1
vin:"123456"
zone:"A"
}
]
But for some reason is AsyncStorage.getItem("vehicle_slot").then(json => console.log(JSON.parse(json)) only the second object, the first one is not added to storage.
Any advice?
your XMLHttpRequest is going to run asynchronously. It's perfectly possible that your code might get to the
AsyncStorage.setItem('vehicle_slot', JSON.stringify(result_test), () => s_cb())
before the onload event has occurred, since that only happens when the request is done. You should add the setItem as a callback.
resultToUpload.forEach(result => {
if (result.picturepath) {
// handle stuff here
let xhr = new XMLHttpRequest();
xhr.onload = (e) => {
// handle other stuff
result_test.push(/* data */);
await storeInAsyncStorage(result_test, () => s_cb());
};
} else {
// handle even more stuff
result_test.push(/* different data */);
await storeInAsyncStorage(result_test, () => s_cb());
}
});
function storeInAsyncStorage(data, callback) {
if(callback) {
return AsyncStorage.setItem(JSON.stringify(data), callback);
} else {
return AsyncStorage.setItem(JSON.stringify(data));
}
}
You should also be aware that AsyncStorage.setItem is asynchronous. The item does not get set immediately, and the setItem method returns a promise that resolves when the item is set. Try using await AsyncStorage.setItem if you're not passing it into some other function.

Contentful content-management (node lib) : entry won't update

Title says all. I think I've tried all possible combinations but none of them seem to work.
What am I doing wrong ?
First :
client.getSpace('<SPACEID>')
.then((space) => {
space.updateEntry({
"sys": {
id: "<ENTRYID>",
version:45
},
"fields": {
"job": {
"fr-FR": "blablabla"
}
}
})
})
=> Unhandled promise rejection TypeError: space.updateEntry is not a function
Second :
client.getSpace('<SPACEID>')
.then((space) => {
space.getEntry(<ENTRYID>)
.then((entry) => {
var ver = entry.sys.version
var id = entry.sys.id
entry = {
"sys":{
id: id,
version:ver
},
"fields": {
"job": {
"fr-FR": "blablabla"
}
}
}
entry.update()
})
})
=> got entry.update() is not a function
Third :
.then((space) => {
space.getEntry(entryId)
.then((entry) => {
entry.fields.job = {"fr-FR": "blabla"}
entry.update()
})
})
=> got Exception '-[__NSCFNumber length]: unrecognized selector sent to instance 0xb0000000000002d3'
Fourth :
entry.fields.job['fr-FR'] = 'blabla'
=> same exception
ExceptionsManager.js:78 Exception '-[__NSCFNumber length]: unrecognized selector sent to instance 0xb000000000000013' was thrown while invoking sendRequest on target RCTNetworking with params (
{
data = {
string = "{\"fields\":{\"fireid\":{\"fr-FR\":\"bla\"},\"commission\":{\"fr-FR\":\"bla\"},\"dep\":{\"fr-FR\":\"bla\"},\"desc\":{\"fr-FR\":\"bla\"},\"email\":{\"fr-FR\":\"thp#ggg.com\"},\"firstname\":{\"fr-FR\":\"firstname\"},\"job\":{\"fr-FR\":\"blabla\"},\"name\":{\"fr-FR\":\"name\"},\"tel\":{\"fr-FR\":\"0675234573\"},\"type\":{\"fr-FR\":\"Collaborateur\"}}}";
trackingName = unknown;
};
headers = {
accept = "application/json, text/plain, */*";
authorization = "Bearer TOKEN";
"content-type" = "application/vnd.contentful.management.v1+json";
"x-contentful-user-agent" = "contentful-management.js/1.3.1";
"x-contentful-version" = 1;
};
incrementalUpdates = 0;
method = PUT;
responseType = text;
timeout = 0;
url = "https://api.contentful.com:443/spaces/<SPACEID>/entries/<ENTRYID>";
},
139
)
to update an entry you can do the following :
First, you need the entry object either, you create it or get and entry by id :
// You can get an Entry object by
// 1. Creating one
var myEntry
space.createEntry({}).then((entry) => {myEntry = entry})
// 2. Get an existing one
space.getEntry('ENTRY_ID').then((entry) => {myEntry = entry})
// to Update an entry you can do the following
entry.fields.name['en-US'] = 'Blog Post'
entry.update()
.then(entry => console.log(entry.fields.name['en-US']))
again it depends on the fields of your entry, you might not need to do [en-US] if you don't have localized content