I'm developing an API with Laravel. In one of the endpoint I'm accessing, some fields are showing a null value, but it should have some information.
Note the "addicionais_descricao" and "valor" fields, both always come with null values when I include them in the attributeitems array, but if I leave it at the initial level, the data is presented, but it doesn't solve my case, because I need this information with the attribute items:
enter image description here
This is where the endpoint calls, I make the query in the "Attribute" table, which has a relationship with the "Attributeitems" table, while the "attributeitems" table is linked to "Attribute" and "product".
public function show($id)
{
$atributos = Atributo::query('atributo')
->select(
'atributo.id',
'atributo.atrdescricao',
'atributoitens.atributo_id',
'atributoitens.produto_id',
'produto.prodescricao',
'produto.provalor'
)
->leftJoin('atributoitens', 'atributo.id', '=', 'atributoitens.atributo_id')
->leftJoin('produto', 'produto.id', '=', 'atributoitens.produto_id')
->where('atributo.id', '=', $id)
->get()->unique('id');
return AtributoResource::collection($atributos);
}
Resource Atributo:
public function toArray($request)
{
return [
'id' => $this->id,
'descricao' => $this->atrdescricao,
'atributoitens' => AtributoitensResource::collection($this->atributoitens),
];
}
Resource Atributo Itens:
public function toArray($request)
{
return [
'id' => $this->id,
'atributo' => $this->atributo_id,
'produtos' => $this->produto_id,
'adicionais_descricao' => $this->prodescricao,
'valor' => $this->provalor
];
}
What is the correct procedure for this situation?
Take this example as a reference :
Controller
$data = $shop->products()
->whereStatus(true)
->where('product_shop.active', true)
->where('product_shop.quantity', '>=', $this->min_product_qty)
->paginate(50);
return (new ProductCollection($data))
->response()
->setStatusCode(200);
ProductCollection
public function toArray($request)
{
return [
'data' => $this->collection
->map(function($product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
'brand' => $this->when($request->brand, $request->brand)
];
}
ProductResource
public function toArray($request)
{
return [
'type' => 'product',
'id' => (string) $this->id,
'attributes' => [
'uuid' => $this->uuid,
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'thumb_path' => $this->thumb_path,
'cover_path' => $this->cover_path,
],
'relationships' => [
'brand' => $this->brand
]
];
}
Something like this should help you do what you want. I cant exactly do it for you. by the way why you are not using Eloquent, something like
Attribute::where(...)->with(['relation_1', 'products'])->get();
public function toArray($request)
{
return [
'id' => $this->id,
'attributes' => [...],
'products' => $this->collection
->map(function($this->product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
];
}
Related
I'm just learning laravel. I want update key / value in database with laravel api but not work.
My products model is one to many with ProductMeta and many to many with contents model.
My Models
class Product extends Model
{
use HasFactory;
protected $guarded = [];
public function productMeta()
{
return $this->hasMany(ProductMeta::class);
}
public function content()
{
return $this->belongsToMany(Content::class, 'product_contents')->withTimestamps();
}
}
class ProductMeta extends Model
{
use HasFactory;
protected $guarded = [];
public function products()
{
return $this->belongsTo(Product::class);
}
}
class Content extends Model
{
use HasFactory;
protected $guarded= [];
public function product()
{
return $this->belongsToMany(Product::class, 'product_contents');
}
Controller
public function update(Request $request, $id)
{
$product = Product::findOrFail($id);
DB::table('product_metas')
->upsert(
[
[
'product_id' => $product->id,
'key' => 'name',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'price',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'amount',
'value' => $request->name,
],
],
['product_id','key'],
['value']
);
return \response()->json([], 204);
}
Table Structure
API parameter
I tried with update and updateOrcreate and updateOrInsert and upsert methods.
just in upsert method writed database but inserted new data.not updated.
In your case, you should use updateOrCreate() instead of upsert.
Product::updateOrCreate([
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]);
or
Product::upsert([
[
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]
], ['product_id'], ['name', 'price', 'amount']);
In addition your problem is your table name is not matching with your structure table name. In your controller DB::table('product_metas') should be DB::table('products_meta').
my problem solved this way:
ProductMeta::query()->where('product_id', $id)->upsert([
['product_id' => $id, 'key' => 'name', 'value' => $request->name],
['product_id' => $id, 'key' => 'price', 'value' => $request->price],
['product_id' => $id, 'key' => 'amount', 'value' => $request->amount]],
['product_id'], ['value']);
$contentRecord = Product::find($id);
$contentRecord->content()->update(['path'=>$request->path]);
return response()->json([], 204);
I forget use query() method for ProductMeta and added $table->unique(['product_id', 'key']); to product meta migration.
**products relation one to many with product Meta
And Many to many with content.
Hi I'm trying to get data and mapping it in resources, but somehow I'm getting error ErrorException: Trying to get property name of non-object. I'm already define the relationship in models, but somehow I'm still getting the error.
User Models
public function shop()
{
return $this->hasOne(Shop::class);
}
Shop Models
public function user()
{
return $this->belongsTo(User::class);
}
And I'm using the resources (ShopCollection) in controller when getting data like this
public function index()
{
return new ShopCollection(Shop::all());
}
This is how I mapping the data in ShopCollection
public function toArray($request)
{
return [
'data' => $this->collection->map(function($data) {
return [
'id_toko' => $data->id,
'name' => $data->name,
'user' => [
'name' => $data->user->name,
'email' => $data->user->email,
'avatar' => $data->user->avatar,
'avatar_original' => $data->user->avatar_original
],
'logo' => api_asset($data->logo),
...
];
})
];
}
Sorry about my english and thank you for your kind help
ErrorException: Trying to get property name of non-object
This very common error is also pretty simple to debug: somewhere, one of your variables is null but you are still trying to use one of it's property.
It is very likely, from what we can see in your code, that $data->user returns null at some point.
It's hard to tell when and where since you didn't provided the line, but I'm confident that's the case.
Since your resource is a Collection, Laravel iterates through it.
You can catch the problematic entry by adding:
public function toArray($request)
{
return [
'data' => $this->collection->map(function($data) {
if($data->user == null){
dd($data);
};
return [
'id_toko' => $data->id,
'name' => $data->name,
'user' => [
'name' => $data->user->name,
'email' => $data->user->email,
'avatar' => $data->user->avatar,
'avatar_original' => $data->user->avatar_original
],
'logo' => api_asset($data->logo),
...
];
})
];
}
This is answer assumes the problem is from $data->user. If that's not the case, edit your question with the full error stack trace.
Thanks to #toyi answer I can found out what is the problem.
The reason why I got the error is because some of shops have null user_id. So to handle it I check it first before using it.
This is the code:
public function toArray($request)
{
return [
'data' => $this->collection->map(function($data) {
$user = $data->user ? [
'name' => $data->user->name,
'email' => $data->user->email,
'avatar' => $data->user->avatar,
'avatar_original' => $data->user->avatar_original
] : [];
return [
'id_toko' => $data->id,
'name' => $data->name,
'user' => $user,
'logo' => api_asset($data->logo),
...
I have made all Relationships and they work. I just wanted to add it, to my API with this code
$devices = Device::with(['Devicemodel' => function($query)
{
$query->with(['Manufacture', 'Devicetype']);
}])
Because Device has a relationship with Devicemodel. This works. And Devicemodel has a relationship with Manufacture and Devicetype. But i get returned NULL. Why?
EDIT:
I forgot to tell, i have a resource, and i return this
public function toArray($request)
{
//return parent::toArray($request);
return [
'id' => $this->id,
'devicetype' => $this->devicetype,
'value1' => $this->value1,
'value2' => $this->value2,
'value3' => $this->value3,
'manufacture' => $this->manufacture,
'devicemodel' => $this->devicemodel,
'remark' => $this->remark
];
}
if i do only return parent::toArray($request); it works.
I am trying to get comment and it's replies in laravel api,I tried following way and faced to this error, how can I solve it?
this is my controller:
public function postSinglePage(Request $request)
{
$post_id=$request->get('post_id');
$post = Post::where('id',$post_id)->where('status', '=', 1)->get();
return response([
"posts" => GetPostsSinglePageData::collection($post),
]);
}
and this is my resource collection to get post and it's comments:
public function toArray($request)
{
return [
'post_id' => $this->id,
"name" => $this->post_title,
"excerpt" => $this->post_excerpt,
"thumbnail" => $this->getFirstMediaUrl("images"),
"logo" => $this->getFirstMediaUrl("images"),
'comment_count'=>getCommentCount($this->id),
'comments'=> new CommentCollection(Comment::select('*')->where('post_id',$this->id)),
"like" => DB::table("like_tb")->select('like_dislike')
->where("type_like",'=','post')
->where('related_id',$this->id)
->get(),
"share" => DB::table("share_tb")->select("share_number")
->where('type','=','post')
->where("related_id",$this->id)
->get(),
];
}
this is comment collection:
public function toArray($request)
{
return [
'commentId'=>$this->id,
'courseId'=>$this->post_id,
'commentDate'=>$this->date,
'commentText'=>$this->body,
'userId'=>$this->user_id,
'replays'=>ReplyCommentCollection::collection(\App\Models\Comment\ReplyComment::where('comment_id',$this->id)->get()),
'users'=>UserInfoCollection::collection(User::where('id',$this->user_id)->get()),
];
}
$this->id not work in inner other function you must write like this:
public function toArray($request)
{
$comments=Comment::select('*')->where('post_id',$this->id)->get();
$commentCollection= CommentCollection::collection($comments);
return [
'post_id' => $this->id,
"name" => $this->post_title,
"excerpt" => $this->post_excerpt,
"thumbnail" => $this->getFirstMediaUrl("images"),
"logo" => $this->getFirstMediaUrl("images"),
'comment_count'=>getCommentCount($this->id),
'comments'=> $commentCollection,
"like" => DB::table("like_tb")->select('like_dislike')
->where("type_like",'=','post')
->where('related_id',$this->id)
->get(),
"share" => DB::table("share_tb")->select("share_number")
->where('type','=','post')
->where("related_id",$this->id)
->get(),
];
}
i'm working on a graphql API using Laravel GraphQL.
As shown in the documentation "Privacy" section, it should be possible to add callback function to a GraphQLType fields privacy attribute. The field is supposed to return null, when the callback returns false.
Similar to the example in the laravel graphql Docs, i've added a privacy callback like so:
public function fields(): array {
return [
'email' => [
'type' => Type::string(),
'description' => 'The email of user',
'privacy' => function(User $user): bool {
return $user->isMe();
}
],
];
}
It appears to me, that this callback function never gets called.
I read something about a possible requirement, that i should resolve my query using the $getSelectFields function to query the $fields manually $with the selected columns. But unfortunately the $select
public function resolve($root, $args, $context, ResolveInfo $info, Closure $getSelectFields) {
$fields = $getSelectFields();
$with = $fields->getRelations(); // empty array
$select = $fields->getSelect(); // empty array
return User::select($select)->with($with)->get();
}
In my case this does not make any difference.
In my query resolver i do as following:
public function resolve($root, $args, $context, ResolveInfo $info, Closure $getSelectFields) {
/** #var SelectFields $fields */
$fields = $getSelectFields();
$select = $fields->getSelect();
$with = $fields->getRelations();
exit(var_dump($fields)); // #RESULT
}
My result looks like this:
object(Rebing\\GraphQL\\Support\\SelectFields)#4668 (2) {
[\"select\":\"Rebing\\GraphQL\\Support\\SelectFields\":private]=> array(0) {}
[\"relations\":\"Rebing\\GraphQL\\Support\\SelectFields\":private]=> array(0) {}
}
So my question is: "How do i use the privacy attribute callback in Laravel Rebing GraphQL?"
I'm using:
PHP 7.3
Laravel 7.17
Rebing Graphql Laravel 5.1
Thanks in advance,
greets Jules
Some more Details about my use case
EpUser.php
namespace App\GraphQL\Type;
use App\CommunityImage;
use App\User;
use Carbon\Carbon;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Facades\Auth;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Type as GraphQLType;
class EpUser extends GraphQLType {
protected $attributes = [
'name' => 'EpUser',
'description' => 'A type',
'model' => User::class,
];
public function fields(): array {
return [
'id' => [
'type' => Type::nonNull(Type::int()),
'description' => 'The id of the user',
'privacy' => function(User $user): bool {
return false;
}
],
'email' => [
'type' => Type::string(),
'description' => 'The email of user',
'privacy' => function(User $user): bool {
return $user->isMe();
}
],
'firstName' => [
'type' => Type::string(),
'description' => 'The firstName of user'
],
'lastName' => [
'type' => Type::string(),
'description' => 'The lastName of user'
],
'fullName' => [
'type' => Type::string(),
'description' => 'The fullName of user',
'selectable' => false,
'resolve' => function(User $user) {
return $user->firstName . " " . $user->lastName;
}
],
'gender' => [
'type' => Type::string(),
'description' => 'The gender of the user'
],
'isOnline' => [
'type' => Type::boolean(),
'description' => '',
'selectable' => false,
'resolve' => function(User $user, $args) {
return $user->isOnline();
}
]
];
}
[...]
And this is the UsersQuery which should respond with a user pagination object, that contains an array of users with a privacy attribute:
UsersQuery.php
namespace App\GraphQL\Query;
use App\Artist;
use App\FilePath;
use Closure;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Query;
use Illuminate\Support\Facades\Auth;
use GraphQL\Type\Definition\ResolveInfo;
use Rebing\GraphQL\Support\Facades\GraphQL;
use App\User;
class UsersQuery extends Query {
protected $attributes = [
'name' => 'UsersQuery',
'description' => 'A query',
'model' => User::class,
];
public function type(): Type {
return GraphQL::type('userPagination');
}
public function authorize($root, array $args, $ctx, ResolveInfo $resolveInfo = NULL, $getSelectFields = NULL): bool {
return Auth::check();
}
public function args(): array {
return [
'id' => [
'type' => Type::int(),
'description' => 'The id of the user'
],
'slug' => [
'type' => Type::string(),
'description' => 'The slug of the user'
],
'pagination' => [
'type' => Type::nonNull(GraphQL::type('paginationInput')),
'description' => 'The pagination of the users to query',
'rules' => 'required',
],
'search' => [
'type' => Type::string(),
'description' => 'a string to search for users'
],
'roles' => [
'type' => Type::listOf(Type::string()),
'description' => 'The roles of the user',
'rules' => 'sometimes|required|array|in:user,developer,administrator'
]
];
}
public function resolve($root, $args, $context, ResolveInfo $info, Closure $getSelectFields) {
if(isset($args['id']) || isset($args['slug'])) {
if(isset($args['slug'])) {
$user = User::where('slug', $args['slug'])->first();
} else {
$user = User::find($args['id']);
}
return [
'items' => $args['pagination']['limit'] > 0 && $user ? [$user] : NULL,
'itemTotal' => $user ? 1 : 0
];
}
$sortBy = $args['pagination']['sortBy'] ?? 'id';
$sortByDesc = isset($args['pagination']['sortByDesc']) ? $args['pagination']['sortByDesc'] : true;
$sortByType = $sortByDesc ? 'desc' : 'asc';
$search = false;
if(isset($args['search']) && $args['search']) {
$search = true;
$query = User::search($args['search']);
} else {
$query = User::query();
}
if(!empty($sortBy)) {
$query->orderBy($sortBy, $sortByType);
}
// Todo: eloquent search can't serach for whereHas
if(isset($args['roles']) && !$search) {
if(is_array($args['roles'])) {
foreach($args['roles'] as &$role) {
$query->whereHas('roles',
function($q) use ($role) {
$q->where('name', $role);
});
}
} else {
$query->whereHas('roles',
function($q) use ($args) {
$q->where('name', $args['roles']);
});
}
}
if($search) {
$userPaginationObject = [
'itemTotal' => $query->count(),
'items' => $query->getWithLimitAndOffset($args['pagination']['limit'],
$args['pagination']['offset'])
];
} else {
$userPaginationObject = [
'itemTotal' => $query->count(),
'items' => $query->limit($args['pagination']['limit'])->offset($args['pagination']['offset'])->get()
];
}
return $userPaginationObject;
}
}