I want to acquire the relation value with the store when searching for a category in Laravel5.6 - php

thank you view my question.
I would like to retrieve information on the tag table relation with the store with many-to-many when searching for a category
I created Store-table, Category-table, Tag-table.
The store-table and the category-table are connected by a many-to-many relation. The tag-table is the same.
I was able to search for categories and get information on businesses that are relation- ed, but I do not know how to get information on tags that are relations with stores.
So, I try this idea. search categories → get storeID from relation data→ storeID search → return shop data that hit.
However, I do not know how to get storeID in the store data acquired by category search
How can I write the code?
please help me.
sorry, bat my English.
App\Store
use Illuminate\Database\Eloquent\Model;
class Store extends Model
{
protected $fillable = ['name','location', 'price', 'open_time',
'closed_day'];
protected $table = 'stores';
public function photos(){
return $this->hasMany(StorePhoto::class);
}
public function categories(){
return $this->belongsToMany(Category::class,'category_store','category_id','store_id');
}
public function tags(){
return $this->belongsToMany(Tag::class, 'store_tag', 'tag_id', 'store_id');
}
}
App\Category
protected $fillable = ['store_id', 'category_id'];
public function stores()
{
return $this->belongsToMany(Store::class,'category_store','store_id','category_id');
}
App\Tag
protected $fillable = ['store_id', 'tag_id'];
public function stores()
{
return $this->belongsToMany(Store::class, 'store_tag', 'store_id', 'tag_id');
}
Resource/Category
class Category 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,
'store' => $this->stores,
];
}
}
web.php
use App\Category;
use App\Http\Resources\Category as CategoryResource;
Route::get("/store/api/category", function (Request $request) {
$search_category = $request->get('category_id');
return new CategoryResource(Category::find($search_category));
});

You can use dot notation to eager load nested relations:
$category = Category::with('stores.tags')->find($request->get('category_id'));
The tags will then be accessible on each Store model related to the Category:
// create a single flattened array of all the tags
$tags = $category->stores->flatMap->tags;

Related

How to retrieve a record from a parent table based on its id on a child table

I have two related models in a job listing application, Company and Listing. The relationship between them is that company may have listing and a listing must have exactly one company.
class Company extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array<int, string>
*/
protected $fillable = [
'name',
'description',
'email',
'website',
'logo',
'address',
'city',
'state',
];
//Relationship to Listing
public function listings(){
return $this->hasMany(Listing::class, 'company_id');
}
//Relationship to company_image
public function company_image(){
return $this->hasMany(CompanyImage::class, 'company_id');
}
//Relationship to User
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
}
The listing model is defined as
class Listing extends Model
{
use HasFactory;
//Relationship to User
public function user(){
return $this->belongsTo(User::class, 'user_id');
}
//Relationship to Company
public function company(){
return $this->belongsTo(Company::class, 'company_id');
}
I tried
public function edit(Listing $listing)
{
$cid = $listing->only(['id']); //to get the id of the company from the listings table
$cid = $cid['id'];
$comp = Company::orderby('name','Asc')->get(); // this list all company in a select field
$company = Company::whereHas('listings', function ($query) { //to get record of the company using the $cid from the listings table
$query->where('listings.id','=',$cid);
})->get();
dd($company); //to check the value returned.
// return view('listings.edit',[
// 'listing' => $listing,
// 'company' => $company,
// 'companys' => $comp
// ]);
}
i get an Undefined variable $cid when i use it like so where('listings.id','=',$cid).
i get null when i use it like so where('listings.id','=','$cid').
I want to get a result like
SELECT companies.name, companies.logo FROM companies join listings on listings.company_id = companies.id where listings.id = 4
which looks like:
enter image description here
You get the error because you need to pass the variable to closure.
You can pass the variable using use($variable) after function()
$company = Company::whereHas('listings', function ($query) use ($cid){
$query->where('listings.id','=',$cid);
})->get();
Just use your current code and access listing as below:
$some_id = 1;
$data = App\Models\Listing::find($some_id);
//For Name
$data->name;
//For images
$data->company->company_image;

Laravel 5.8: Display eloquent items sorted based on timestamp

I'm not sure, how this is called, so I'll explain it as good as possible.
I've a ticket system, where I display all comments in one section. In a different section, I display related information like "Supporter changed", "Ticket title changed", "Status of ticket changed" and so on.
Current rendered (unstyled) HTML: https://jsfiddle.net/2afzxhd8/
I would like to merge these two sections into one, that those related information are displayed between the comments of the ticket. Everything (comments + related information) should be displayed sorted based on the created_at timestamp.
New target rendered (unstyled) HTML: https://jsfiddle.net/4osL9k0n/
The ticket system has in my case these relevant eloquent models (and tables):
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Tickets extends Model
{
use SoftDeletes;
protected $fillable = [
'tracking_number', 'customer_id', 'category_id',
'priority_id', 'subject', 'status_id', 'is_done',
'supporter_id'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function status() {
return $this->belongsTo(TicketStatuses::class, 'status_id');
}
public function priority() {
return $this->belongsTo(TicketPriorities::class, 'priority_id');
}
public function category() {
return $this->belongsTo(TicketCategories::class, 'category_id');
}
public function supporter() {
return $this->belongsTo(User::class, 'supporter_id');
}
public function operations() {
return $this->hasMany(TicketOperations::class, 'ticket_id');
}
public function comments() {
return $this->hasMany(TicketComments::class, 'ticket_id');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class TicketComments extends Model
{
use SoftDeletes;
protected $fillable = [
'ticket_id', 'text', 'user_id', 'is_html',
'email_reply', 'internal_only'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function ticket() {
return $this->belongsTo(Tickets::class, 'id', 'ticket_id');
}
public function user() {
return $this->belongsTo(User::class, 'user_id');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class TicketOperations extends Model
{
use SoftDeletes;
protected $fillable = [
'ticket_id', 'user_id', 'ticket_activity_id',
'old_value', 'new_value'
];
protected $hidden = [
];
protected $dates = ['deleted_at'];
public function ticket() {
return $this->belongsTo(Tickets::class, 'ticket_id');
}
public function activity() {
return $this->belongsTo(TicketActivities::class, 'ticket_activity_id');
}
public function user() {
return $this->belongsTo(User::class, 'user_id');
}
}
Please don't care about the CSS - it is styled in my case. It's just not relevant here.
Any idea, how I need to update my view to be able to build my target HTML?
As per my understanding, you have data that retrieved from multiple models.
So what you can do is to, merge the informations into a new array:
For example, consider the data regarding the ticket history is being stored in an array named:
$arrTicketHistory;
And consider, that the information regarding the ticket updates is being stored in an array named:
$arrTicketUpdates;
Merge these two arrays and assign the result in another array, say:
$arrDatesAndIDs;
Now try sorting the array $arrDatesAndIDs on the basis of timestamp i.e. created_at. Then display the result with a simple for loop.
You can add a custom parameter in the arrays $arrTicketUpdates and $arrDatesAndIDs, just for the sake of uniqueness. It might help you to identify which type of information it is, regarding the ticket.
You can use the array function array_msort(), a php function, to sort a multidimensional array.
I just found this answer, but this one has one big issue: It overwrites in worst-case some objects with different objects and this results in possible missing objects in the collection.
From the Laravel documentation: Collections:
The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection.
Due to this, I had to update the logic to this:
$ticket = Tickets::where('tracking_number', '=', $request->tracking_number)->first();
$comments = $ticket->comments;
$operations = $ticket->operations;
$history_unsorted = new Collection();
$history_unsorted = $history_unsorted->merge($comments);
$history_unsorted = $history_unsorted->merge($operations);
$history = $history_unsorted->sortBy('created_at');
This avoids, that the original collection gets overwritten.
With this, I can simply loop over $history:
#foreach($history as $history_item)
#if ($history_item instanceof App\TicketOperations)
<!-- Ticket Operation -->
#else
<!-- Ticket Comment (Text) -->
#endif
#endforeach

Conditionally append attribute to model in laravel

Is it possible to append an attribute to my model whenever a model scope is called?
For example in my controller I want to call a scope to append those dynamic attribute like :
$Media_query = OutDoorMedia::query();
$Media_query->orderby('created_at', 'desc');
$Media_query->PreviouslyOrdered();
$Media = $Media_query->get();
And in my model I want to do something like :
class OutDoorMedia extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id',
'user_id',
'address',
'location',
'media_type',
];
}
class scopePreviouslyOrdered extends OutDoorMedia
{
public $appends = ['previously_ordered'];
public function getPreviouslyOrderedAttribute()
{
if ($this->hasMany('App\Models\OutDoorMediaOrders', 'odm_id', 'id')->Where(function ($query) {
$query->where('status', MEDIA_ORDER_CHECKOUT_STATUS)
->orWhere('status', STATUS_TO_PAY);
})->exists()) {
return true;
} else {
return false;
}
}
}
But it's not working and I know it's wrong, How to achieve this?
I solved this problem with help of #apokryfos but with a bit tweak. hope this reduce wasting others time.
Instead of appending attributes on the model I have appended the said attribute to my model by the eloquent magic method :
$Media_query = OutDoorMedia::query();
$Media_query->orderby('created_at', 'desc');
$Media = $Media_query->get()->each(function ($items) {
$items->append('previously_ordered');//add this attribute to all records which has the condition
});
In Model As apokryfos said I have put these two methods:
public function PreviousOrders() {
return $this->hasMany('App\Models\OutDoorMediaOrders', 'odm_id', 'id');
}
public function getPreviouslyOrderedAttribute() {
return $this->PreviousOrders()->exists();
}
But I don't need this method and I had to remove it from the model because if it exist in model it will automatically append to model:
public $appends = [ 'previously_ordered' ];
I think there's a misunderstanding on how scopes should work. A scope is basically like a shortcut query for a model. You are using it to test existance of a relationship but there's a better way to do that using whereHas
Here's how you would achieve this using a relationship:
class OutDoorMedia extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'id',
'user_id',
'address',
'location',
'media_type',
];
public function previousOrders() {
return $this->hasMany('App\Models\OutDoorMediaOrders', 'odm_id', 'id');
}
public function getPreviouslyOrderedAttribute() {
return $this->previousOrders()->exists();
}
}
Then you simply do:
$Media_query = OutDoorMedia::whereHas('previousOrders')
->orderby('created_at', 'desc');
If you what the dynamic attribute appended on the model automatically you can just add the following to the model:
public $appends = [ 'previously_ordered' ];
I guess if you want the best from both worlds you can do:
class OutdoorMediaWithPreviouslyOrdered extends OutDoorMedia {
public $appends = [ 'previously_ordered' ];
}
Then when you need the appending model you can use :
$Media_query = OutdoorMediaWithPreviouslyOrdered ::orderby('created_at', 'desc');

How can I get all the users that has commented a post?

I'm working with Laravel 5 and I've the following Models
PostComment.php
class PostComment extends Model
{
protected $fillable = [
'post_group_id', 'user_id', 'comment_content'
];
public function post(){
return $this->belongsTo('App\PostGroup');
}
public function user(){
return $this->belongsTo('App\User');
}
}
PostGroup.php
class PostGroup extends Model
{
protected $fillable = [
'group_id', 'user_id', 'post_content'
];
public function user(){
return $this->belongsTo('App\User');
}
public function group(){
return $this->belongsTo('App\Group');
}
public function commented(){
return $this->hasMany(
'App\PostComment'
);
}
}
Group.php
class Group extends Model
{
protected $fillable = ([
'id'
]);
public function users(){
return $this->belongsToMany(
'App\User',
'user_group'
);
}
public function members(){
return $this->belongsToMany(
'App\User',
'user_group'
)->wherePivot('state','accepted');
}
public function posted(){
return $this->hasMany(
'App\PostGroup'
);
}
}
My web application presents groups, in which you can create posts and in which post you can write comments. In my database I've the following relationships:
Group: (id, name, description);
PostGroup: (id, group_id, user_id, post_content);
PostComment: (id, post_group_id, user_id, comment_content);
What I want to do is to create a collection of User objects, and then make a query to get all users, subscribed to a group, who have commented on a certain post, in MySQL looks like:
select users.* from users, post_comments where users.id = post_comments.user_id and post_comments.post_group_id="1"
So in my controller I've the following code
$theGroup = Group::find($groupId);
$thePost = PostGroup::find($postId);
$memberList = User::where('id', '<>', Auth::user()->id)->whereIn('id', $theGroup->users->pluck('id'))->
So, what I want to do is to extend that query to get the desidered result with ->get()->sortBy('last_name');, how can I exted it after the whereIn?
EDIT
User.php
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token','created_at','updated_at'
];
public function groups(){
return $this->belongsToMany('App\Group','user_group');
}
public function groupsAsAdmin(){
return $this->belongsToMany('App\Group','user_group')->wherePivot('role','admin');
}
public function groupsAsMember(){
return $this->belongsToMany('App\Group','user_group')->wherePivot('state','accepted');
}
public function groupsAsInvited(){
return $this->belongsToMany('App\Group','user_group')->wherePivot('state','pending');
}
public function posted(){
return $this->hasMany('App\PostGroup');
}
public function commented(){
return $this->hasMany('App\PostComment');
}
}
From your description, you already come up with a list of Users in advance, so that you only will find Posts with a Comment of these specific users.
Basically, what you want is to use whereHas($relation, $calback) to perform the checks you described:
$userIds = [2, 3, 5, 7, 11, 13, 17]; // or query them...
$postId = 123; // the id of the post where we want to look through the comments
User::where('id', '<>', Auth::id())
->whereIn('id', $userIds)
->whereHas('comments', function ($query) use ($postId) {
$query->where('post_group_id', $postId);
})
->get();
This will simply check if a user has written a Comment for the given post. Because you forgot to post your User model, I assumed that there is a relation available for the comments of the user.
You could also combine the first two conditions (user in list, but not the authenticated one) into one, if you want. $userIds = array_diff($userId, [Auth::id()]) does the job. where('id', '<>', Auth::id()) can be dropped from the query then.
If you do also need to check for an active subscription of the user to a group, it will be slightly more complex. But as you commented, you are already finding only users for a group, so this should be fine.
In PostComment, try this
$this->selectRaw(‘user_id, comment_content’)->where(‘post_group_id’, 1)->groupBy(‘user_id’)->get();

Laravel models to implement one to many and many to many in single query

i have this table structure, project has one to many relation with rewards , rewards and shipping has many to many relation with pivot table reward_ship.
projects rewards shipping reward_ship
--------- -------- -------- ------------
id id id id
title amount location reward_id
amount project_id name ship_id
i am trying to extract one particular project details with all other associate tables data(rewards and shipping data using reward_ship table) in one query.
These is how i am trying
Projects Model
class Rewards extends Model {
public function projs(){
return $this->hasMany('App\Rewards');
}
public function rewds(){
return $this->belongsToMany('App\Shipping')
->withPivot('reward_ship', 'ship_id', 'reward_id');
}
public function shiplc(){
return $this->belongsToMany('App\Rewards')
->withPivot('reward_ship', 'ship_id', 'reward_id');
}
}
class Rewards extends Model {
public function proj() {
return $this->belongsTo('App\Projects');
}
}
Controller api class
Route::get('projects/{id}', function($id) {
$p = Projects::find($id);
$getd = Rewards::with('proj')
->where('rewards.project_id', '=', $p->id)
->get();
});
it doesn't work.
i search and tried many related model base query in larvel.
i know my implementation are wrong. Please suggest me to work out.
You can use Laravel 5.5 new feature API Resources.
It helps you to format the output of objects such as models or collections, to display attributes and also relationships.
So, you could do something like this in your ItemResource:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class Project extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request
* #return array
*/
public function toArray($request)
{
return [
'project_id' => $this->project_id,
'title' => $this->title,
'amount' => $this->amount,
// To access relationship attributes:
'rewards' => $this->rewards->load('shippings'),
];
}
}
Then in your controller, you just need to create a new Resource instance and pass the item object that you want to return:
use App\Http\Resources\Project as ProjectResource;
// some code
/**
* Show a single formatted resource.
*
* #param Project $project
* #return ProjectResource
*/
public function show($project)
{
return new ProjectResource($project);
}
// the rest of your code
The output should be the expected.
You have to fix the relationships that you have :
Projects Model :
public function rewards(){
return $this->hasMany('App\Rewards');
}
Rewards Model :
public function projects() {
return $this->belongsTo('App\Projects');
}
public function shippings(){
return $this->belongsToMany('App\Shipping','reward_ship', 'reward_id', 'ship_id');
}
Shipping model:
public function rewards(){
return $this->belongsToMany('App\Rewards','reward_ship', 'ship_id', 'reward_id');
}
After that you can call the relationships in the controller to eager load the wanted elements like this :
$project = Projects::with('rewards.shippings')
->where('id', $project_id)
->get();
And in the view you can loop over the rewards then get the shippings like this :
#foreach ($project->rewards as $reward)
<p>This is a reword {{ $reward->amount }}</p>
#foreach ($reward->shippings as $shipping)
<p>This is a shipping {{ $shipping->name }}</p>
#endforeach
#endforeach
class Project extends Model
{
public function rewds()
{
return $this->hasMany('App\Rewards');
}
public function shiplc()
{
return $this->hasManyThrough('App\Shipping', 'App\Rewards');
}
}
class Rewards extends Model
{
public function shiplc()
{
return $this->belongsToMany('App\Shipping');
}
public function projs()
{
return $this->belongsTo('App\Project');
}
}
class Shipping extends Model
{
public function shiplc()
{
return $this->belongsToMany('App\Shipping');
}
}
Route::get('projects/{id}', function($id) {
$p = Projects::with(['rewds', 'shiplc'])->find($id);
});
Project.php
class Project extends Model {
public function rewards() {
return this->hasMany(Reward::class, 'project_id', 'id');
}
}
Reward.php
class Reward extends Shipping {
public function shipping(){
return $this->belongsToMany(Shipping::class, 'reward_ship', 'reward_id', 'ship_id');
}
public function project(){
return $this->belongsTo(Project::class);
}
}
You can retrieve it like this:
$projectDetails = Project::where('id', $projectId)
->with(['rewards', 'rewards.shipping'])->get();

Categories