Laravel how to validation with unique JSON fields type? - laravel

I have a POST endpoint on my Laravel 5.7 API and Mariadb 10.3, I used postman to test my route and here is the problem. I want to send an array of the same kind of object, like this :
{
"Shops":[
{
"name": {
"en":"ShopEng",
"es":"ShopESP"
},
"code": "0891"
}
]
}
In my database.I have Shops table and Name field as JSON type.
{"en":"TestEng","es":"TestESP"}
In my Request. I also tried with the simple but not work.
public function rules()
{
return array(
'Shops' => 'required|array',
'Shops.*.name.en' => 'required|unique:shops,name->en',
'Shops.*.name.es' => 'required|unique:shops,name->es',
'Shops.*.code' => 'required|integer'
);
}
With Message.
"message": "Method Illuminate\\Validation\\Validator::validateUnique,shops,name>en does not exist.",
"exception": "BadMethodCallException",
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Validation/Validator.php",

The second parameter written in the unique validation rule refers to a column on the database table where the unique rule will be tested, I'm afraid the syntax you wrote there is not valid.
My guess here is that you will have yo create a custom validation rule for handling this particular case.

Related

Directus - Retrieve relations with GraphQL

I try to retrieve the relations of the database of my directus app.
I use GraphQL with following endpoint https://myapp.directus.app/graphql.
Using a query for the relations according to the docs
query {
relations {
collection
field
}
}
leads to an validation error:
{
"errors": [
{
"message": "GraphQL validation error.",
"extensions": {
"code": "GRAPHQL_VALIDATION_EXCEPTION",
"graphqlErrors": [
{
"message": "Cannot query field "relations" on type "Query". Did you mean "locations"?",
"locations": [
{
"line": 2,
"column": 2
}
]
}
]
}
}
]
}
I use Postman so far and started with a query for the database entries
query {
boards {
id
columns {
id
name
}
}
}
which works as expected.
What I find interesting is, that Postman shows an mouseover text on the keyword "relations" of the 2nd query -->
"Cannot query field relations on type Query. Did you mean locations?"
But unfortunately I have no idea what to do with that message.
Any ideas what is wrong here?
Meanwhile I found the problem. The endpoint for queries on relations is https://myapp.directus.app/graphql/system
With that the query
query {
relations {
collection
field
}
}
is working

Laravel Test intermittently failing due to 1 second difference

I have the following type of test for most models across my application:
* #test
*/
public function an_authorised_user_can_delete_a_contact()
{
$contact = Contact::factory()->create();
$this->assertDatabaseHas('contacts', $contact->getAttributes());
$response = $this->actingAs($this->user_delete)
->delete('/contacts/'.$contact->id);
$this->assertSoftDeleted('contacts', $contact->getAttributes());
$response->assertRedirect('contacts');
}
This works well most of the time, but every now and then will fail due to the timestamps being slightly out.
Failed asserting that any soft deleted row in the table [contacts] matches the attributes {"first_name":"Katherine","last_name":"Will","title":"Jewelry Model OR Mold Makers","telephone":"+15127653255","mobile":"+19366193055","email":"lucy.lind#example.net","vendor_id":1,"updated_at":"2022-04-04 18:09:50","created_at":"2022-04-04 18:09:50","id":1}.
Found: [
{
"id": 1,
"first_name": "Katherine",
"last_name": "Will",
"title": "Jewelry Model OR Mold Makers",
"telephone": "+15127653255",
"mobile": "+19366193055",
"email": "lucy.lind#example.net",
"vendor_id": 1,
"created_at": "2022-04-04 18:09:50",
"updated_at": "2022-04-04 18:09:51",
"deleted_at": "2022-04-04 18:09:51"
}
]
The difference is in updated_at - "2022-04-04 18:09:50" vs "2022-04-04 18:09:51".
Is there a better way to structure the test to make it more robust?
1st solution:
Use Carbon::setTestNow('2022-04-04 18:09:50') , this will make timestamps not change.
2nd solution (recommended):
Since you wrote the test to check if an authorized user can delete a contact I would not check all attributes as you did but instead would assert the model itself, which is recommended on laravel documentation, here's the link:
$this->assertSoftDeleted($contact);
or only check with the id
$this->assertSoftDeleted('contacts', [
'id' => $contact->id
]);
You can also check out this answer on laracast forum maybe it well help you more.
After deleting the contact record, you should fetch a fresh instance of the model. Try this:
...
$deletedContact = $contact->fresh();
$this->assertSoftDeleted('contacts', $deletedContact->getAttributes());
$response->assertRedirect('contacts');
I would also remove $this->assertDatabaseHas('contacts', $contract->getAttributes()); to make the test run a bit faster. When writing tests you should be testing your own code, not the Laravel framework.
In your factory try to override the "created_at" attribute an hour ago.
public function definition()
{
return [
'created_at'=> $this->faker->dateTimeBetween('-1 hour' );,
'updated_at'=> \Carbon\Carbon::now()->timestamp;
];
}

Can I create a single validation error bag for Lighthouse PHP, include GraphQL validation issues

Is there a way when using Lighthouse PHP to get the GraphQL validation error to appear side by side with the Laravel validation errors? Currently the graph QL validations cause a different type of error, before the Laravel error are run.
For example if I submit the following mutation:
mutation register(
password: 123456
) {
id
}
I will get back just an error back
{ "message": "Variable username not present. Expected type String",
"extensions": {
"category": "graphql"
}
}
Then when I submit:
mutation register(
username: somethingStupid
password: 123456
) {
id
}
Then I will get this error:
{ "message": "Validation failed for the field [register].",
"extensions": {
"validation": {
"username": [
"Username must be unique."
]
},
"password": [
"Password must contain letters and numbers"
]
},
"category": "validation"
},
}
For my front end I'd like them only to have to expect a single error bag, but this seems to require them to expect two different types of errors. Assuming my Laravel validations cover the schema definition, I would like for them never to see the GraphQl errors. Or am I missing the concept of GraphQL somehow?

Laravel Fractal add success message to response

Is it possible to add a success message to a JSON response using Fractal? I would like the structure to look like the following
{
"success": true,
"message": "Clients found",
"_metadata": {
"total_count": 2
},
"data": {
"clients": [
]
}
}
I have written the following code to return the data
$response_data = fractal()
->collection($person_array)
->transformWith(new ResponseTransformer())
->toArray();
Do I need to create a new serialiser to get this format? I have been following this documentation https://packagist.org/packages/spatie/fractalistic but there is no option to add extra key values such as success: true.
I also want to use this format for all my API responses, is it possible to create a generic Transformer which returns what ever array data I pass into it instead of creating a Transformer per Model?
Laravel fractal allows to add meta data to collections :
fractal()
->addMeta([
'status' => [
'success' => true,
'code' => 1,
'message' => 'Test'
]
])
Resulting JSON output
"meta": {
"status": {
"success": true,
"code": 1,
"message": "Test"
}
}

Get all Data in a Body using Guzzle in Laravel from External API

I want to retrieve only the email in a JSON response using guzzle in Laravel from an external API. Here is what I have tried
//Get all customer
$allcus = 'https://api.paystack.co/customer';
$client = new Client();
$response = $client->request('GET', $allcus, [
'headers' => [
'Authorization' => 'Bearer '.'sk_live_#########################',
],
]);
$cus_data = json_decode($response->getBody()->getContents());
//returns a json response of all customers
//dd($cus_data);
$cus_data_email = $cus_data->data->email;
dd($cus_data_email);
Using this returns error
$cus_data_email = $cus_data->data->email;
"message": "Trying to get property 'email' of non-object"
But when I tried this, it returns the customer in the first array
$cus_data_email = $cus_data->data[0]->email;
I don't want to return just one customer email. I want to retrieve all of the customers' emails.
This is the way the JSON response is
{
"status": true,
"message": "Customers retrieved",
"data": [
{
"integration": ######,
"first_name": null,
"last_name": null,
"email": "a###$gmail.com",
"phone": null,
"metadata": null,
"domain": "live",
"customer_code": "CUS_##########",
"risk_action": "default",
"id": #######,
"createdAt": "2020-05-26T00:50:12.000Z",
"updatedAt": "2020-05-26T00:50:12.000Z"
},
...
What you're looking for is a loop!
$cus_data->data is an array, which is a variable that can store multiple values at once. These can be accessed with an index, and are commonly seen with loops.
I highly suggest reading the two links I supplied, the loop I'll be using is a foreach loop, as it's the most readable in this context. All the loops have their place, so it would pay to get familiar with them.
$emailsArray = []; // initialise an array
$emailsString = ""; // initialise a string
// Here's our loop, which will go over all the values of $cus_data->data
foreach($cus_data->data as $datum) {
// $datum is the single value in $cus_data->data which we're currently looking at
// Each of these values have an email property, which we access with arrow notation
array_push($emailsArray, $datum->email); // add the email to our array
$emailsString = $emailsString . $datum->email . ", "; // add the email to our string
}
Following this, $emailsArray will be an array (like we learned about above!) with all the emails from $cus_data->data.
$emailsString will contain the same information, just in a comma-separated string.
One thing to watch out for is if some of your data doesn't have emails! Then the code above could fail.
Admittedly, this isn't the shortest solution. For a problem like this, I would probably use array_map. The code here does the same thing in a more verbose format so we can understand it better.

Resources