I'm using Laravel 5 and MongoDB based Eloquent Jenssegers to develop an API to save and get data. I have a object called Player and inside I have other nested objects.
For example:
{
"idPlayer": "1",
"name": "John",
"lastname": "Doe",
"stats": {
"position": "lorem",
"profile": "ipsum",
"technique": {
"skill": 1
}
}
}
Using Postman to test I've could insert "idPlayer", "name" and "lastname" without problems, but I couldn't figure out how to insert stats inside the Player object.
This is what I've tried:
PlayerController.php
public function store(Request $request)
{
$player->name= $request->input('name');
$player->lastname = $request->input('lastname');
$player->save();
return response()->json($player);
}
And to insert stats I've tried to do something like this inside the store function:
$player->stats = $request->input('position');
$player->stats = $request->input('profile');
But I get "Stats:null" on response and the name and lastname inserts ok.
I expect to insert the data just as the Player object shown above.
Make an array with keys.
public function store(Request $request)
{
$player->name = $request->input('name');
$player->lastname = $request->input('lastname');
$player->stats = [
'position' => $request->input('stats.position'),
'profile' => $request->input('stats.profile'),
];
$player->save();
return response()->json($player);
}
More data about PHP arrays.
Retrieving input from Laravel Requests.
Related
I have a many to many relationship between Users & Courses with a pivot table, Users_Courses, containing an isComplete value, but i can't seem to retrieve the isComplete value without looping through every user, getting their courses and then looping over every course getting the pivot data.
All the examples i have found is to map the isComplete value to the course with loops, but that seems like it's awfully taxing on the program and i don't really find it appealing which is why I'm making my own question here. If there's already an answer to this that i haven't seen please link it below as i can't seem to find it.
Also, I'm using Laravel-9 and MySQL.
The data structure I'm retrieving right now looks like this:
"data": [
{
"id": 2,
"fname": "name",
"lname": "last name",
"email": "mail#mail.com",
"courses": [
{
"id": 1,
"name": "test_course_1",
"description": "this is a test course for testing"
},
{
"id": 2,
"name": "test_course_2",
"description": "this is also a test course"
},
{
"id": 3,
"name": "test_course_3",
"description": "this course is a test course"
}
]
}
]
I'm searching for a way to retrieve the pivot value isComplete with Eloquent and getting the data with the course itself like this or something like it.
In other words, I want to check if the user has completed the course or not through the pivot table value isComplete as shown in the example below.
"data": [
{
"id": 2,
"fname": "name",
"lname": "last name",
"email": "mail#mail.com",
"courses": [
{
"id": 1,
"name": "test_course_1",
"description": "this is a test course for testing",
"isComplete": 1
},
{
"id": 2,
"name": "test_course_2",
"description": "this is also a test course",
"isComplete": 0
},
{
"id": 3,
"name": "test_course_3",
"description": "this course is a test course",
"isComplete": 0
}
]
}
]
The code i have right now looks like this:
class User extends Authenticatable
{
public function courses()
{
return $this->belongsToMany(Course::class, 'user_courses')
->withPivot('isCompleted');
}
}
class Course extends Model
{
public function users()
{
return $this->belongsToMany(User::class, 'user_courses')
->withPivot('isCompleted');
}
}
class UserController extends Controller
{
public function getUsersById(int $user_id)
{
try {
$users = User::where('id', $user_id)
->with('courses')
->get();
return response()->json([
'success' => true,
'data' => $users
]);
} catch (Throwable $th) {
return response()->json([
'success' => false,
'data' => null,
'message' => $th,
]);
}
}
}
I am aware that it's called isCompleted in the code, but it's also called that in the database. It's a typing error which haven't yet been fixed :D
In other words, I want to check if the user has completed the course or not through the pivot table value isComplete as shown in the example below.
Did you read about filtering using Pivot table columns in the docs: https://laravel.com/docs/9.x/eloquent-relationships#filtering-queries-via-intermediate-table-columns
If you need only completed courses you can call relation as
$users = User::where('id', $user_id)
->with(['courses' => function($query) {
$query->wherePivot('isCompleted', 1); // use quotes if its datatype is enum in database.
}])
->get();
Or you can make customized relations for completed, Incompleted in your Model.
class User extends Authenticatable
{
public function courses()
{
return $this->belongsToMany(Course::class, 'user_courses')
->withPivot('isCompleted');
}
public function completedCourses()
{
$this->courses()->wherePivot('isCompleted', 1);
}
public function InCompleteCourses()
{
$this->courses()->wherePivot('isCompleted', 0);
}
}
And in user controller you can call them as
$users = User::where('id', $user_id)
->with('completedCourses')
->get();
if you want the output to be like the JSON:
$user = User::with("courses")->find(1);
$user = $user->courses->each(
function($course) {
$course->isComplete = $course->pivot->isComplete;
unset($course->pivot);
}
);
this line will retrieve Courses with an object pivot, which includes the columns of your pivot table.
(Example)
I have a laravel 8 Unit Test which looks like this:
public function testAddingTwoCars()
{
$response = $this->postJson('api/basket', ['cars' => [['name' => 'car one'], ['name' => 'car two']]]);
$response
->assertStatus(200)
->assertJson(['total' => 40]);
}
In my route I have:
Route::post('/api/basket',[basketController::class, 'store']);
In my controller I have:
public function store(Request $request)
{
$data = $request->all();
return response()->json($data);
}
when I run the test with php artisan test it shows this:
• Tests\Unit\BasketTest > adding two cars
Unable to find JSON:
[{
"total": 40
}]
within response JSON:
[{
"cars": [
{
"name": "car one"
},
{
"name": "car two"
}
]
}].
How do I:grab the total as its not in $request so that I could do something like this in my controller:
$public function store(Request $request)
{
$data = $request->all();
$data['total'] = 40;
return response()->json($data);
}
The above works, but it is hard coded so is not the best way to do it
assertJson
Assert that the response contains the given JSON data:
$response->assertJson(array $data, $strict = false);
The assertJson method converts the response to an array and utilizes PHPUnit::assertArraySubset to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON response, this test will still pass as long as the given fragment is present.
You can read about it at this link
https://laravel.com/docs/8.x/http-tests#assert-json
and Here
https://laravel.com/docs/5.4/http-tests#testing-json-apis
and you can check this video too
https://adamwathan.me/2016/11/16/the-only-json-assertion-youll-ever-need/
->assertJson(['total' => 40]) is non-sense; this will not pass unless the JSON has it:
return response()->json(['data' => $data, 'total' => 40]);
I'm developing an api for a social media like. This app has a follow-unfollow system. I did my follower list and follower list query and response . But, I wanna add follow info(boolean) in response. How to solve clear this problem? (Sorry for my bad English)
I want this response:
"data": [
{
"user_id": 3,
"username": "example",
"name": "Example",
"photo": "default.jpg",
"is_following": true
}
]
Current response:
"data": [
{
"user_id": 3,
"username": "example",
"name": "Example",
"photo": "default.jpg",
}
]
User model in follow relationship:
function follows(){
return $this->belongsToMany(self::class,"follows","followed_id","following_id")->select("users.id","username","name","photo");
}
function followers(){
return $this->belongsToMany(self::class,"follows","following_id","followed_id")->select("users.id","username","name","photo");
}
My Resource:
public function toArray($request)
{
return [
"user_id" => $this->id,
"username" => $this->username,
"name" => $this->name,
"photo" => $this->photo(),
"is_following" =>
];
}
In your User model define an accessor that return true/false with your "following" logic.
public function getIsFollowingAttribute()
{
if (condition)
return true;
return false;
}
After this add this to User model to include it in array and JSON representations
protected $appends = ['is_following'];
Read this
I asked this kind of question the other day and I was trying to do it with two tables, but a friend said that I could solve it with one table. If you wish, you can look at the location and look at the problem and its solution.
Laravel API JSON customization and table relationship
I'm quite new to Laravel and I was not able to find the answer to this problem neither on Laravel docs, nor here.
I guess it's just a matter of how to search for it, cause I'm pretty sure it's a common case.
I have two models in relationship (this is a simplified case), I retrieve the info I need through a Resource file, but I'm not able to understand how to properly store or update info.
Here's a code example:
Models\Company.php
class Company extends Model
{
protected $fillable = [
'name', 'blablabla', 'country_id', 'blablabla2',
];
public function country() {
return $this->belongsTo(Country::class);
}
}
Models\Country.php
class Country extends Model
{
protected $fillable = [
'code', 'name', 'prefix', 'tax_code_id',
];
public function companies() {
return $this->hasMany(Company::class);
}
}
Then I have a CompanyController file to manage API requests:
Controllers\CompanyController.php
class CompanyController extends BaseController
{
public function index()
{
$companies = Company::paginate();
$response = CompanyResource::collection($companies)->response()->getData(true);
return $this->sendResponse($response, 'Companies retrieved successfully');
}
public function store(Request $request)
{
$input = $request->all();
$validator = Validator::make($input, $this->validation_rules);
if($validator->fails()){
return $this->sendError('Validation error.', $validator->errors());
}
$company = Company::create($input);
return $this->sendResponse($company->toArray(), 'Company added successfully.');
}
}
...
public function update(Request $request, Company $company)
{
$input = $request->all();
$validator = Validator::make($input, $this->validation_rules);
if($validator->fails()){
return $this->sendError('Validation error.', $validator->errors());
}
$company->update($input);
return $this->sendResponse($company->toArray(), 'Company updated successfully.');
}
And here the CompanyResource I'm using to display info as I need.
Resources/CompanyResource.php
class CompanyResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'blablabla' => $this->blablabla,
'country' => $this->country,
'blablabla2' => $this->blablabla2,
];
}
}
So when retrieving Companies (or single company) I get a nested JSON:
{
"id": "1",
"name": "something",
"blablabla": "blablabla",
"country": {
"id": "100",
"code": "MA",
"name": "Mars",
"prefix": "123",
"tax_code_id": "#$%"
},
"blablabla2": "blablabla2"
}
If I create or update a new company I send a payload that has the same structure of what I'm getting above, but if I edit country id value my company model doesn't get it.
PUT Api/companies/1
{
"name": "something",
"blablabla": "blablabla3",
"country": {
"id": "200",
"code": "JU",
"name": "Jupiter",
"prefix": "456",
"tax_code_id": "#=%"
},
"blablabla2": "blablabla2"
}
I'm expecting to update country_id field in companies table for record 1 so that it matches payload (so going from 100 to 200), but it's not happening.
I could edit frontend logic in order to send only country_id in payload since I'm not going to update countries table and all that additional info is redundant, but I'd like to know how to manage it in controller with Laravel.
Would you mind helping me? Thanks in advance.
If you want it to work with the code now, you need to have country_id in the root JSON object you are sending. As this is the way you would fill the id. This is not the best approach in my opinion, but this is why your update is not working at the moment.
{
"name": "something",
"blablabla": "blablabla3",
"country_id": 200,
...
I actually like the approach of sending complete objects. Commonly to fill id's is not good, as it can interfere with the way relations work. Laravel will set your relationships when you associate, if not you are not guaranteed to have the correct relationship after the fill.
Therefor i would fetch out the id and associate the country object with the company. In a logic similar to this.
// only fill non relation fields, fill used as save is needed after associate()
$company->fill($request->only(['name', 'blabla']));
$company->country()->associate(Country::find($request->get('country')['id']));
//associate does not save
$company->save();
I wrote a gist for this years ago that can relate any two models regardless of their relationship type. You just need to supply it with the name of the relationship method: https://gist.github.com/kmuenkel/055f107139d904e30810bf53750d9c6e
What I am trying to do is to send a JSON array (that was gotten from Guzzle) to my SQL database. I have gotten to the point where I am able to get the response and display the gotten JSON array on a webpage. The array is defined as the $data variable. The $data variable gets decoded using this:
$data = json_decode($response->getBody()->getContents());
This is able to get the JSON and decode it with no problem. The part I am stuck on is taking the $data variable, processing it and sending it to my database. From what I understand is that you are required to convert the JSON into an array and then send it to the database.
The JSON format is like this:
[{
"INTLDES": "2017-042Z",
"NORAD_CAT_ID": "42848",
"OBJECT_TYPE": "TBA",
"SATNAME": "OBJECT Z",
"COUNTRY": "TBD",
"LAUNCH": "2017-07-14",
"SITE": "TTMTR",
"DECAY": null,
"PERIOD": "96.52",
"INCLINATION": "97.61",
"APOGEE": "597",
"PERIGEE": "586",
"COMMENT": null,
"COMMENTCODE": null,
"RCSVALUE": "0",
"RCS_SIZE": null,
"FILE": "6242",
"LAUNCH_YEAR": "2017",
"LAUNCH_NUM": "42",
"LAUNCH_PIECE": "Z",
"CURRENT": "Y",
"OBJECT_NAME": "OBJECT Z",
"OBJECT_ID": "2017-042Z",
"OBJECT_NUMBER": "42848"
}]
My Satellite Model goes like this:
protected $fillable = [
'intldes',
'norad_cat_id',
'object_type',
'satname',
'country',
'launch',
'site',
'decay',
'period',
'inclination',
'apogee',
'perigee',
'comment',
'commentcode',
'rcsvalue',
'rcs_size',
'file',
'launch_year',
'launch_num',
'launch_piece',
'current',
'object_name',
'object_id',
'object_number'
];
My migrations file:
Schema::create('satellites', function (Blueprint $table) {
$table->increments('id');
$table->string('intldes');
$table->string('norad_cat_id');
$table->string('object_type');
$table->string('satname');
$table->string('country');
$table->string('launch')->nullable();
$table->string('site')->nullable();
$table->string('decay')->nullable();
$table->string('period')->nullable();
$table->string('inclination')->nullable();
$table->string('apogee')->nullable();
$table->string('perigee')->nullable();
$table->string('comment')->nullable();
$table->string('commentcode')->nullable();
$table->string('rcsvalue')->nullable();
$table->string('rcs_size')->nullable();
$table->string('file')->nullable();
$table->string('launch_year')->nullable();
$table->string('launch_num')->nullable();
$table->string('launch_piece')->nullable();
$table->string('current')->nullable();
$table->string('object_name');
$table->string('object_id');
$table->string('object_number');
$table->timestamps();
});
I tried making an $object array, which did not work.
TL;DR: I want to take the $data variable, which contains the decoded JSON and create something that allows it to get saved into my 'satellites' SQL database.
EDIT: Here is the full Satellite controller:
public function displayer(){
$api = new Client([
'base_uri' => 'https://www.space-track.org',
'cookies' => true,
]); $api->post('ajaxauth/login', [
'form_params' => [
'identity' => '#',
'password' => '#',
],
]);
$response = $api->get('basicspacedata/query/class/satcat/orderby/INTLDES%20desc/limit/5/metadata/false');
$data = json_decode($response->getBody()->getContents(), true);
$data = array_change_key_case($data, CASE_LOWER);
$model = Satellite::create($data);
dd($data);
}
It looks like your JSON key names match up nicely with your model attributes, with the exception of being capitalised.
Try mapping the data keys to lowercase and then creating your model instance.
Per #OmisakinOluwatobi suggestion, you can use pass true to json_decode to retrieve the data as an array.
Edit - I missed that your response data was an array of objects. The following update will iterate over the response data and create a new Satellite for each.
// Retrieve data from response
$data = json_decode($response->getBody()->getContents(), true);
// Iterate over response data
foreach ($data as $attributes) {
// Change attribute keys to lowercase
$attributes = array_change_key_case($attributes, CASE_LOWER);
// Create satellite model
Satellite::create($attributes);
}
It is actually as simple as $encoded = json_encode($request->your_array);. This $encoded will now be "savable" to sql database. When you later access the encoded data, you can use a JSON parser to convert back to a json array. Example using jQuery var your_array = $.parseJSON($response.body);