Laravel, Follow-Unfollow System Issue - php

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

Related

Eloquent: Get specific data from pivot table when retrieving all

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)

How to update foreign key ide value in Laravel Eloquent?

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

How to insert nested objects into MongoDB with Laravel 5?

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.

Laravel: Return attribute from a relation and not the entire object

I'm developing and API with Laravel, but I have a problem or question with relations 1:N (one to many). Is there posible to only show one attribute just like this, where creatorUser is my relation.
{
"id": "string",
"production": "string",
"title": "string",
"description": "string",
"resourceType": "string",
"collections": "string",
"creatorUser: "name"
}
creatorUser is my relation and at this moment i'm getting like this
{
"id": "string",
"production": "string",
"title": "string",
"description": "string",
"resourceType": "string",
"collections": "string",
"creatorUser: {
id: "string",
name: "string"
}
}
I don't want to retrieve the data from creatorUser like object, just the name and be showed like this "creatorUser: "name" Is there any way?
and I'm retrieving my info like this (This is my controller)
return CTL_Resource::with(['creationCountry' => function ($query) {
$query->select('idCountry', 'name');
}, 'creatorUser' => function ($query) {
$query->select('idUser', 'name');
}, 'resourceType' => function ($query) {
$query->select(['idResourceType', 'name']);
}, 'tags' => function ($query) {
$query->select(['idTag' => 'name']);
}, 'quickTags' => function ($query) {
$query->select(['idQuickTag' => 'name']);
}, 'relatedTo' => function ($query) {
$query->select(['idRelatedTo' => 'name']);
}])->orderBy('createTime', 'DESC')->paginate($request->per_page);
thanks for help.
(don't ask why id's are string, hahaha, its because they are UUIDs) :P
Inside of the model that is called by creatorUser.. "App\User" .. use the protected variable $hidden set as an array and pass the field name (or attribute name) into the array to hide the id of that model in the json return object...
So for example:
Say I have a model named Foo and inside of the Foo model I have this getting returned:
{
foo_order: 039,
foo_id: 12982,
foo_name: "Bar"
}
In this example I want to get rid of, or hide, foo_id from the returned json object. Then inside of my "App\Foo" model I'd simple add in:
namespace app;
use whatever\goes\here;
class FooModel extends Model
{
...
...
/*
* Hide the listed fields (Attributes)
* from the returned json object
*/
protected $hidden = array('foo_id');
}
the result SHOULD be the following when you "get" results from this specific table/model:
{
foo_order: 039,
foo_name: "Bar"
}
For more information on a deeper level to modify your returned json results please review the following doc:
https://laravel.com/docs/5.4/eloquent-mutators

Laravel 5.1 and Fractal: including pivot table data on the transformer

Tables: contact, company and a relationship table with a custom pivot attribute company_contact (company_id, contact_id, is_main)
Company and Contact have a many to many relationship (belongsTo on both models).
Expected output when I retrieve the contacts of a company:
{
"data": [
{
"id": 1,
"name": "JohnDoe",
"is_main": false
},
{
"id": 2,
"name": "JaneDoe",
"is_main": true
}
]
}
Expected output when I retrieve the contact list with ?include=companies:
{
"data": [
{
"id": 1,
"name": "John Doe",
"companies": {
"data": [
{
"id": 501,
"name": "My Company",
"is_main": true
},
{
"id": 745,
"name": "Another Company",
"is_main": false
}
]
}
},
{
"id": 2,
"name": "Jane Doe",
"companies": {
"data": [
{
"id": 999,
"name": "Some Company",
"is_main": true
}
]
}
}
]
}
What's the best way of adding the pivot table attribute? It doesn't seem very clean to add is_main on the company transformer if the attribute is set.
For the first example I was thinking about using parameters ?include=company_relationship:company_id(1) with something like:
public function includeCompanyRelationship(Contact $contact, ParamBag $params) {
// .. retrieve the pivot table data here
$is_main = $company->is_main;
// but now I would need another transformer, when what I actually want is to push the value on the main array (same level)
return $this->item(??, ??);
}
I understand how to retrieve pivot data (related: Laravel 5.1 - pivot table between three tables, better option?) but not the best way of adding it in the https://github.com/thephpleague/fractal Transformer logic.
I already have a ContactTransformer and CompanyTransformer but if I add is_main to the CompanyTransformer all the calls I make (related or not to contacts) will also expect that attribute.
If I'm reading you correctly, you can utilize a single CompanyTransformer to handle whether you wish to have the is_main property set, but only if a $contact parameter is passed in to it's constructor, something along these lines:
class CompanyTransformer extends TransformerAbstract
{
public function __construct(Contact $contact = null)
{
$this->contact = $contact;
}
public function transform(Company $company)
{
$output = [
'id' => $company->id,
'name' => $company->name,
];
if($this->contact) {
// This step may not be necessary, but I don't think the pivot data
// will be available on the $company object passed in
$company = $this->contacts->find($company->id);
// You may have to cast this to boolean if that is important
$output['is_main'] = $company->pivot->is_main;
}
return $output;
}
}
Then in your includeCompanyRelationship just pass in the new CompanyTransformer with the parameter:
public function includeCompanyRelationship(Contact $contact)
{
$companies = $contact->companies;
return $this->collection($companies, new CompanyTransformer($contact));
}
This should work whether you're calling your companies endpoint directly, or calling the contact's endpoint while embedding the company relationship data.
I know this is older, but I just ran into this issue. Here is how I resolved it.
Added the withPivot to the relationships, in my case category and users.
In the CategoryTransformer, I defined my includeUsers method:
/**
* Include Users
* #param Category $category
* #return \League\Fractal\Resource\Collection
*/
public function includeUsers(Category $category)
{
# Just Add withPivot Here.
$users = $category->users()->withPivot('role')->get();
return $this->collection($users, new UserTransformer);
}
Then in your UserTransformer class, on the transform() method:
public function transform($user)
{
return [
'username' => $user['username'],
'lastname' => $user['lastname'],
// ... truncated
'role' => isset($user['pivot']) ? $user['pivot']['role'] : null,
]
}
Then when I call my api for categories and include users I get:
{
"data": [
{
"name": "Network",
"description": "some description of category here.",
"users": {
"data": [
{
"id": 1,
"username": "jborne",
"firstname": "Jason",
"lastname": "Borne",
"title": "Department Head",
"company": "Borne Inc",
"email": "jason#somedomain.com",
"display_name": "Jason Borne",
"mobile": "555-5555",
"active": true,
"role": "IM",
}
]
}
}
]
}
As you see, I get the role relationship I wanted from the pivot. Otherwise you just get null for that field. Still not ideal, but much less messy in my opinion.

Categories