I am making a forums system with post likes, but for some reason, the relationship for the post likes doesn't return any data.
I have tried loading the data without the resource, and included with('all_likes') but this still doesnt return the data.
My get posts method:
public function getPosts($id)
{
$response = array();
$response['posts'] = ForumPostResource::collection(ForumPost::where('thread_id', $id)->get());
return $response;
}
My Forum Post Resource:
class ForumPostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'user_id' => $this->user_id,
'thread_id' => $this->thread_id,
'body' => $this->body,
'like_data' => $this->all_likes,
'user_data' => new UserResource($this->user),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at
];
}
}
My forum post model:
class ForumPost extends Model
{
protected $fillable = [
'user_id', 'thread_id', 'body', 'likes', 'created_at', 'updated_at',
];
protected $appends = ['likes_total', 'user_data'];
public function user()
{
return $this->belongsTo('App\User');
}
public function all_likes()
{
return $this->hasMany('App\PostLike', 'id', 'post_id');
}
public function getCreatedAtAttribute($value)
{
return date('D, d F Y G:i', strtotime($value));
}
public function getUserDataAttribute()
{
return $this->user()->first();
}
public function getLikesTotalAttribute()
{
return $this->all_likes()->count();
}
}
Once you click on the like button the user id and post id are added to the post_likes database. Then when the view is loaded which displays all forum posts including the post likes relationship data.
The result I get is this:
{
"posts": [
{
"id": 14,
"user_id": 1501,
"thread_id": 3,
"body": "<p>Welcome everyone</p>",
"like_data": [],
"user_data": {
"name": "mikelmao",
"role_id": 1,
"avatar": "users/default.png"
},
"created_at": "Sat, 05 January 2019 13:04",
"updated_at": {
"date": "2019-01-11 03:22:27.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
]
}
This should be returning 1 post like result as my db looks like this:
id|user_id|post_id
1|1501|14
I had overlooked the fact that it was calling App\PostLike and the foreign key and local key were in the wrong order.
Method definition is:
public function hasMany($related, $foreignKey = null, $localKey = null)
Related
good afternoon!
I would like some help from them regarding the scenario below:
I have 3 tables, 1 is the Moviment which is the main one, the 2 is the cart_moviments which is the secondary one which when I enter the movement data, it saves some data in cart_moviments and I have the 3 table called vehicles, which I have all the vehicle information, all of them have a foreignId, just to relate them.
So on my return from the resource, I have Moviment->cartMoviment, and I want to access it from cartMoviment ->vehicles, which I can't, because it doesn't relate.
My code Store:
public function store(StoreMovimentRequest $request, StoreCartMovimentRequest $requestCart, StoreDocumentRequest $requestDocument)
{
$this->authorize('create', Moviment::class);
$moviment = Moviment::create($request->validated());
$moviment->cartMoviment()->create($request->validated());
foreach(array($request['key_number']) as $notasFiscais) {
foreach($notasFiscais as $notas) {
$moviment->document()->create($notas);
}
}
return new MovimentResource($moviment);
}
My CartMoviment MOdel:
class CartMoviment extends Model
{
use HasApiTokens, HasFactory, Notifiable, UserTenant;
protected $fillable = ['type', 'vehicle_cart_id'];
// protected $table = 'cart_moviments';
public function Moviment() {
return $this->belongsTo(Moviment::class);
}
public function Vehicle() {
return $this->belongsTo(Vehicle::class, 'vehicle_cart_id');
}
My Moviment Model:
class Moviment extends Model
{
use HasApiTokens, HasFactory, Notifiable, UserTenant;
protected $fillable = [
'document_type_id', 'people_id', 'company_id', 'vehicle_id', 'department_id'
];
public function Vehicle() {
return $this->belongsTo(Vehicle::class);
}
public function cartMoviment() {
return $this->belongsToMany(Moviment::class, cartMoviment::class);
}
`
My resource Array:
public function toArray($request)
{
return [
'id' => $this->id,
// 'count' => $this->count(),
'type' => $this->type,
'department_id' => $this->department_id,
'person_id' => $this->people_id,
'person_name' => $this->person->name,
'vehicle_id' => $this->vehicle_id,
'vehicle_board' => $this->vehicle->board,
'vehicle_status' => $this->vehicle->status,
'vehicle_manufacturer' => $this->vehicle->vehicleModel->name,
'vehicle_type' => $this->vehicle->vehicleType->type,
'cart_moviment' => $this->cartMoviment,
My return resource
"cart_moviment": [
],
"documents": [
],
`
I need relationship , thanks you very match.
I'm working with Laravel 6.x, I have an "Item" class like :
Schema::create('items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('type');
$table->string('subtype');
});
In a controller, I have a route for get all items and return a json response like :
public function getItems()
{
return response()->json([
'datas' => Item::all()
]);
}
This function return this json :
{
"datas": [
{
"id": 1,
"name": "FIRE",
"type": "9",
"subtype": "5"
},
{
"id": 2,
"name": "FIRE",
"type": "9",
"subtype": "5"
}
]
}
I need to dynamically add an uppercase on the first letter of each field before returning the json response, without change the laravel field name and the migration. I need to keep the field in lowercase in Laravel.
What I need is :
{
"datas": [
{
"Id": 1,
"Name": "FIRE",
"Type": "9",
"Subtype": "5"
},
{
"Id": 1,
"Name": "FIRE",
"Type": "9",
"Subtype": "5"
}
]
}
How can I do that simply ? I have a lots of class with a lots of fields in real. Thanks !
You could use the Eloquent: API Resources like this:
php artisan make:resource Item
then you will get a class in the Resources folder. That's where you edit the keys
Resources/Item.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class Item extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'Id' => $this->id,
'Name' => $this->name,
'Type' => $this->type,
'Subtype' => $this->subtype,
];
}
}
In your controller, import the newly created resource and use it.
Controllers/ItemController.php
use App\Http\Resources\Client as ClientResource;
public function getItems()
{
return response()->json([
'datas' => ItemResource::collection(Item::all());
]);
}
There you go!
You can define an additional getter in the model for the name attribute (with a new name). So you can have both name and the new attribute that you define. Use ucfirst along with strtolower.
public function getUCFirstNameAttribute($value)
{
return ucfirst(strtolower($value));
}
OR
You can define an Items Resource. Inside the toArray method, you can use ucfirst on the name field.
return [
...
'name' => ucfirst($this->name),
...
];
Both ways you can keep the original field as is.
Simple and Generic solution :
public function toArray() {
$array = parent::toArray();
$newArray = array();
foreach($array as $name => $value){
$newArray[str_replace('_', '', ucwords($name, '_'))] = $value;
}
return $newArray;
}
Put this in a Model class, and after that just do : Item::all()->toArray()
This function transorm my_awesome_field to MyAwesomeField
I try to include a relationship in my resource array if it has been eager loaded, but don't get it working.
Anyone has an idea, how I can check the relationships in the ResourceCollection?
Database schema looks like this:
Here is my Post Model
class Post extends Model
{
function categories() {
return $this->belongsToMany('App\Category');
}
}
Here is my Category Model
class Category extends Model
{
function posts() {
return $this->belongsToMany('App\Post');
}
}
Here is my Post Controller
Class PostController extends Controller
{
public function index()
{
return new PostResourceCollection(Post::with("categories")->get());
}
}
Here is my Post ResourceCollection
class PostResourceCollection extends ResourceCollection
{
public function toArray($request)
{
return [
'data' => $this->collection->transform(function($page){
return [
'type' => 'posts',
'id' => $page->id,
'attributes' => [
'name' => $page->title,
],
];
}),
//'includes' => ($this->whenLoaded('categories')) ? 'true' : 'false',
//'includes' => ($this->relationLoaded('categories')) ? 'true' : 'false',
];
}
}
Maybe too late, below solution is a workaround for this case:
return [
...,
'includes' => $this->whenLoaded('categories', true),
];
Loading custom attribute:
return [
...,
'includes' => $this->whenLoaded('categories', fn() => $this->categories->name),
];
You relationship is wrong, a post belongs to many categories while a category has many posts so change:
class Category extends Model
{
function posts() {
return $this->belongsToMany('App\Post', 'category_post');
}
}
to
class Category extends Model
{
function posts() {
return $this->hasMany('App\Post', 'category_post');
}
}
Now when you load the post you can load the categories also:
$posts = Post::with('categories')->get();
got it.. That was the missing piece. if anyone has a better solution for this it would be much appreciated.
foreach ($this->collection as $item) {
if ($item->relationLoaded('categories')) {
$included = true;
}
I am having a problem in this laravel insertion code. Whenever a new company is created, I get the output as Company created but with company_name as 0. I don't know why this happens. My code is
Controller:
public function store(Request $request)
{
$Company = new Company([
'company_name' => $request->input('company_name'),
'head'=>$request->input('head'),
'Email'=>$request->input('email'),
'address'=>$request->input('address'),
'phno'=>$request->input('phno'),
'contact_person'=>$request->input('contact_person')
]);
if ($Company->save()) {
$response = [
'msg' => 'Company Created',
'Company' => $Company
];
return response()->json($response, 201);
}
$response = [
'msg' => 'An Error Occurred',
];
return response()->json($response, 502);
}
My Model code is:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
//
protected $table = 'companies';
public $timestamps = false;
protected $primaryKey = 'company_name';
protected $fillable = ['company_name','head','contact_person','phno','email','address'];
}
Input:
{
"company_name":"cachii1",
"head":"sankar",
"email":"sankar#gmail.com",
"address":"chennai",
"phno":"123",
"contact_person":"sankar"
}
output:
{
"msg": "Company Created",
"Company": {
"company_name": 0,
"head": "sankar",
"address": "chennai",
"phno": "123",
"contact_person": "sankar"
}
}
The Database Schema is
The Database Schema is
The Database Schema is
public function up()
{
Schema::create('companies', function (Blueprint $table) {
// $table->increments('company_id');
$table->string('company_name');
$table->primary('company_name');
$table->string('head');
$table->string('contact_person');
$table->bigInteger('phno');
$table->string('email');
$table->text('address');
});
By default, Laravel expects your model primary key to be auto incrementing. From what you've posted, it looks like that's not the case with your example.
Add this to your model definition:
public $incrementing = false;
is it there an easy way to remove the "data" envelope from the Dingo API response.
When I use this Transformer to transform user models:
class UserTransformer extends EloquentModelTransformer
{
/**
* List of resources possible to include
*
* #var array
*/
protected $availableIncludes = [
'roles'
];
protected $defaultIncludes = [
'roles'
];
public function transform($model)
{
if(! $model instanceof User)
throw new InvalidArgumentException($model);
return [
'id' => $model->id,
'name' => $model->name,
'email' => $model->email
];
}
/**
* Include Roles
*
* #param User $user
* #return \League\Fractal\Resource\Item
*/
public function includeRoles(User $user)
{
$roles = $user->roles;
return $this->collection($roles, new RoleTransformer());
}
I get this response:
{
data : [
"id": 102,
"name": "Simo",
"email": "mail#outlook.com",
"roles": {
"data": [
{
"id": 1
"name": "admin"
}
]
}
}
]
}
I read some articles about RESTful APIs and a lot of them stated that such enveloped responses arent very modern (You should use the HTTP Header instead).
How can I disable this behaviour at least for the includes?
Thank you
For those who fall on this later and as I had really hard time to make it, I'd like to share how I made it working in my API :
1) Create a Custom Serializer, NoDataArraySerializer.php :
namespace App\Api\V1\Serializers;
use League\Fractal\Serializer\ArraySerializer;
class NoDataArraySerializer extends ArraySerializer
{
/**
* Serialize a collection.
*/
public function collection($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
/**
* Serialize an item.
*/
public function item($resourceKey, array $data)
{
return ($resourceKey) ? [ $resourceKey => $data ] : $data;
}
}
2) Set new the Serializer. In bootstrap/app.php, add :
$app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Api\V1\Serializers\NoDataArraySerializer);
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
That's it.
Now, in your UserController (for instance), you can use it like this :
namespace App\Api\V1\Controllers;
use App\Api\V1\Models\User;
use App\Api\V1\Transformers\UserTransformer;
class UserController extends Controller
{
public function index()
{
$items = User::all();
return $this->response->collection($items, new UserTransformer());
}
}
And the response will look like :
[
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
Or, I you want to add an enveloppe, you just need to set the resource key in the Controller. Replace :
return $this->response->collection($items, new UserTransformer());
by
return $this->response->collection($items, new UserTransformer(), ['key' => 'users']);
And the response will look like :
{
"users": [
{
"user_id": 1,
...
},
{
"user_id": 2,
...
}
]
}
One addition to the solution of YouHieng. The preferred way to register the NoDataArraySerializer in Laravel 5.3 and above is to write a custom ServiceProvider and add the logic into the boot() method and not the bootstrap/app.php file.
For Example:
php artisan make:provider DingoSerializerProvider
Then:
public function boot(){
$this->app['Dingo\Api\Transformer\Factory']->setAdapter(function ($app) {
$fractal = new League\Fractal\Manager;
$fractal->setSerializer(new App\Http\Serializers\NoDataArraySerializer());
return new Dingo\Api\Transformer\Adapter\Fractal($fractal);
});
}
Have a look to http://fractal.thephpleague.com/serializers/#arrayserializer. They explain exactly what to do when
Sometimes people want to remove that 'data' namespace for items