How to use updateOrCreate proprely - php

Here is my code :
$user = \App\User::where('id', $uid)->firstOrFail();
$user->token()->updateOrCreate(['user_id' => $uid, 'type' => $type], [
'auth' => $auth_token,
'refresh' => $refresh_token,
'type' => $type
]);
I've got two models User and Token with an 'one to one' relationship.
On the first line, I try to catch a User into the database, then I update my model with the updateOrCreate() method.
However, as you can read it, I must use a selector 'user_id' => $uid before to successfully update my model. I think Eloquent should be able to manage it in a different way without making two requests.

You don't have to use the user_id => $uid on that query. It's injected based on the relationship already. You can rewrite that to:
$token = \App\User::where('id', $uid)->firstOrFail()->token()->updateOrCreate(['type' => $type], [
'auth' => $auth_token,
'refresh' => $refresh_token,
'type' => $type
]);

Related

There is nothing happening when store data for Laravel API

I making laravel API where i can store a new data where the value in body raw json. but when i try to send request using post, i got nothing but the status is 200 OK. when i chek my mysql there is no data inputed.
So, what should i do?
mysql data
Laravel Controller, and API,
// function in controller
use App\Models\ChartAge;
class ChartController extends Controller
{
public function saveChart(Request $request)
{
$data = $request->validate([
'entity' => 'required|string|max:10',
'code' => 'required|string|max:10',
'year' => 'required|int|max:10',
'under_age_15' => 'required|string|max:50',
'age_15_64' => 'required|string|max:50',
'age_65_over' => 'required|string|max:50',
]);
$values = ChartAge::create($request);
return response()->json(
[
'status' => true,
'message' => "the videos has been favorites",
'data' => $values,
],
201
);
}
}
//in api.php
Route::post("charts", [ChartController::class, 'saveChart']);
and here is when i tried to send request using postman.
because there is no error, i don't know what's wrong??
First double check your ChartAge model, does it have $fillable or not?
and Edit your code:
From
$values = ChartAge::create($request);
To:
$values = ChartAge::create($request->all());
Hope this will be useful.
With validation:
$data = \Validator::make($request->all(),[
'entity' => 'required|string|max:10',
'code' => 'required|string|max:10',
'year' => 'required|int|max:10',
'under_age_15' => 'required|string|max:50',
'age_15_64' => 'required|string|max:50',
'age_65_over' => 'required|string|max:50',
]);
if($data-> fails()){
return back()->withErrors($data)->withInput();
}
$values = ChartAge::create($request->all());
Do you set fillable fields in your 'ChartAge' model?
protected $fillable = ['entity','code','year'...];
Do you try to test code with disabling validation?
Please try to put dd($request) in the first row of the controller code.
Method create expects a plain PHP array, not a Request object.

How should I use factories in Laravel when I have default values in DB

I have a factory:
$factory->define(\App\MissingData::class, function (Faker $faker) {
$operations = Operation::all()->pluck('id')->toArray();
$operationId = $faker->randomElement($operations);
$operation = Operation::find($operationId);
$meters = $operation->meters->pluck('id')->toArray();
$arrStatus = ['Done', 'Undone'];
return [
'operation_id' => $operationId,
'meter_id' => $faker->randomElement($meters),
'date_ini' => $faker->dateTimeThisYear,
'date_end' => $faker->dateTimeThisYear,
'status' => $faker->randomElement($arrStatus),
];
});
In my migration, I have:
$table->string('status')->default('Undone');
When I want to insert an array in DB, I always prefer to use factory:
factory(MissingData::class)->create($missingData);
with
return [
'operation_id' => $measure->operation_id,
'meter_id' => $measure->meter_id,
'conso_prod' => $measure->conso_prod,
'date_ini' => $missingDataIni,
'date_end' => $missingDataEnd,
];
The wanted behaviour is to insert the status: 'Undone' configured in DB, but my factory will generate a fake status, so I will always have to send Undone status to my factory, which is not the point of using a DB default.
How am I supposed to manage this. Using factory to create and insert model is a good practice.
Using default in DB is also very practical, I believe they can be used both at the same time, but I don't see how should I do that.
Any idea ?
Your best bet is probably to default the status to undone then have a seperate state for done and any other status' that you may add.
$factory->define(\App\MissingData::class, function (Faker $faker) {
$operations = Operation::all()->pluck('id')->toArray();
$operationId = $faker->randomElement($operations);
$operation = Operation::find($operationId);
$meters = $operation->meters->pluck('id')->toArray();
return [
'operation_id' => $operationId,
'meter_id' => $faker->randomElement($meters),
'date_ini' => $faker->dateTimeThisYear,
'date_end' => $faker->dateTimeThisYear,
'status' => 'Undone',
];
});
$factory->state(\App\MissingData::class, 'done', fn() => ['status' => 'Done']);
Then when you want the status to be done you would use the state like this.
factory(\App\MissingData)->state('done')->create();

Laravel phpunit test nested eager loading

I have this nested relation im abit unsure how i assertJson the response within the phpunit test.
FilmController
public function show(string $id)
{
$film = Film::with([
'account.user:id,account_id,location_id,name',
'account.user.location:id,city'
])->findOrFail($id);
}
FilmControllerTest
public function getFilmTest()
{
$film = factory(Film::class)->create();
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'description' => $film->description,
'account' => $film->account->toArray(),
'account.user' => $film->account->user->toArray(),
'account.user.location' => $film->account->user->location->toArray()
]);
}
Obviously this isnt working because its returning every column for the user im a little unfamiliar with how you test nested relations with the code you need so im unsure with a toArray can anyone help out?
Testing is a place where you throw DRY (don't repeat yourself) out and replace it with hard coded solutions. Why? simply, you want the test to always produce the same results and not be bound up on model logic, clever methods or similar. Read this amazing article.
Simply hard code the structure you expect to see. If you changed anything in your model to array approach, the test would still pass even thou your name was not in the response. Because you use the same approach for transformation as testing. I have tested a lot of Laravel apps by now and this is the approach i prefers.
$account = $film->account;
$user = $account->user;
$location = $user->location;
$response->assertExactJson([
'description' => $film->description,
'account' => [
'name' => $account->name,
'user' => [
'name' => $user->name,
'location' => [
'city' => $location->city,
],
],
],
]);
Don't test id's the database will handle those and is kinda redundant to test. If you want to check these things i would rather go with assertJsonStructure(), which does not assert the data but checks the JSON keys are properly set. I think it is fair to include both, just always check the JSON structure first as it would likely be the easiest to pass.
$response->assertJsonStructure([
'id',
'description',
'account' => [
'id',
'name',
'user' => [
'id',
'name',
'location' => [
'id',
'city',
],
],
],
]);

Lumen 5.4: PHPUnit: How to test authorisation?

I am working on an ecommerce project, a generic book shop.
I started out with a Test Driven approach, and I adhered to it fully till now.
Different endpoints on this Lumen Microservice project have been successfully tested earlier to make sure they do CRUD operations. However, as I have to protect the Create, Update and Delete method with token authorisation, I am quite confused how to introduce tests for authorisation.
As of now this is my testing structure:
tests/app/Exceptions/HandlerTest.php
tests/app/Http/Controllers/BooksControllerTest.php
The tests are for index, show, store, update, delete. This is one of the tests:
public function testStoreBookByPost()
{
$book = factory('App\Book')->make();
$this->post(
'/books',
[
'isbn' => $book->isbn,
'title' => $book->title,
'description' => $book->description,
'author' => $book->author,
'image' => $book->image,
'price' => $book->price,
'slug' => $book->slug
]
);
$this
->seeJson(
[
'created' => true
]
)
->seeInDatabase(
'books',
[
'title' => $book->title
]
);
}
I had earlier separated Exception Handler tests, similarly I would prefer to separate the AuthControllerTest to AuthControllerTest.php.
What is the best way to do this?
Do I need to write the authorisation tests by refactoring all the BooksControllerTest?
Or should I just test for issuing of token and inability to manipulate database? Would that be fine?
Short answer: I needed to write the authorisation tests by refactoring all the BooksControllerTest
Long answer: I found out a fantastic way of logging in dummy users during testing.
With that I have created this method.
public function loginWithUserGetJWT()
{
$user = factory('App\User')->create(
[
'password' => bcrypt('366643') // random password
]
);
$content = $this
->post(
'/auth/login',
[
'email' => $user->email,
'password' => '366643'
]
)
->seeStatusCode(200)
->response->getContent();
$token = json_decode($content)->token;
return $token;
}
And I am reusing this method in all the test cases, like so:
public function testStoreBookByPost()
{
$token = $this->loginWithUserGetJWT();
$book = factory('App\Book')->make();
$this->post(
'/books',
[
'isbn' => $book->isbn,
'title' => $book->title,
'description' => $book->description,
'author' => $book->author,
'image' => $book->image,
'price' => $book->price,
'slug' => $book->slug,
'token' => $token
]
);
$this
->seeJson(
[
'created' => true
]
)
->seeInDatabase(
'books',
[
'title' => $book->title
]
);
}

How add value to custom field in ActiveCampaign?

Here is documentation how add custom field to ActiveCampaign https://www.activecampaign.com/api/example.php?call=contact_add
Here is my code in Laravel:
$client = new Client();
$res = $client->request('POST', 'https://domain-example.com/admin/api.php?api_action=contact_add', [
'form_params' => [
'api_key' => 'api_key',
'actid' => 'actid',
'api_action' => 'contact_add',
'api_output' => 'json',
'email' => $_POST['email'],
'field[utm_source, 0]' => $_POST['utm_source'],
'p[123]' => 10,
'field[%REFERRAL_PAGE%, 0]' => $_SERVER['HTTP_REFERER']
]
]);
Result: contact added,but the field utm_source is empty. In Laravel I checked , this property is not empty before send. Other fields are not empty too, the problem is only with custom field utm_source. Any thoughts why it happens?
I think is the next entry in the API what you really should use
field[%utm_source%,0] => $_POST['utm_source']

Categories