DB Laravel get and first in same time - php

I want to show only one result from "links, tags, pages", "->first()", but, I want to show multiple results from "comments", "->get()", separate it in one array, and send to view, how can I do this?
$link = DB::table('links')
->join('tags', 'tags.id', 'links.tag')
->join('pages', 'pages.id', '=', 'links.page_id')
->leftJoin('comments', 'comments.link_id', '=', 'links.id')
->select('links.photo', 'links.id', 'links.description', 'links.country', 'links.url', 'links.suggestions', 'links.shares', 'links.friendly_url', 'links.page_id', 'links.sponsored', 'links.clicks', 'links.title', 'tags.color', 'tags.id as tag_id', 'tags.name', 'pages.name as p_name', 'pages.description as p_description', 'pages.id as p_id', 'pages.followers', 'pages.links', 'pages.friendly_url as p_friendly_url', 'pages.photo as p_photo')
->where('links.friendly_url', $id)
->first();
return view('site.link', compact('link'));
DD($link);
0 => Link {#291 ▼
#relations: array:3 [▼
"page" => Page {#295 ▶}
"tag" => Tag {#289 ▶}
"comments" => Collection {#292 ▼
#items: array:2 [▼
0 => Comment {#299 ▶}
1 => Comment {#301 ▶}
]
}
]

You could use Eloquent. Define relationships:
public function page()
{
return $this->belongsTo(Page::class);
}
public function tag()
{
return $this->belongsTo(Tag::class, 'tag');
}
public function comments()
{
return $this->hasMany(Comment::class);
}
And do this:
Link::with('page', 'tag', 'comments')->where('friendly_url', $id)->first()

Related

Laravel push to collection, changes prepend values to array

I'm following a tutorial to add infinity scroll with livewire, there's other approaches that's working but this one is different because it's using cursorPaginate and pushes the data to a collection. so instead of querying all the data each time you push new data to a collection.
class HomePage extends Component
{
public $posts; // holds are list of posts.
public $nextCursor; // holds our current page position.
public $hasMorePages;
/**
* Initialize data
* #return void
*/
public function mount()
{
$this->posts = collect(); // initialize the data
$this->loadPosts(); // load the data
}
/**
* Load data and maintain cursor state
*
*/
public function loadPosts()
{
if ($this->hasMorePages !== null && !$this->hasMorePages) {
return;
}
$posts = Post::cursorPaginate(
5,
['*'],
'cursor',
Cursor::fromEncoded($this->nextCursor)
);
$this->posts->push(...$posts->items());
$this->hasMorePages = $posts->hasMorePages();
if ($this->hasMorePages === true) {
$this->nextCursor = $posts->nextCursor()->encode();
}
}
}
the first call is woking as expected and returning the collection below:
Illuminate\Support\Collection {#1190 ▼
#items: array:5 [▼
0 => App\Models\Post {#1742 ▶}
1 => App\Models\Post {#1740 ▶}
2 => App\Models\Post {#1739 ▶}
3 => App\Models\Post {#1738 ▶}
4 => App\Models\Post {#1737 ▶}
]
#escapeWhenCastingToString: false
}
but when I scroll or do the second call. the old values are changed to array and then appending the new collection, the second call returns like that:
^ Illuminate\Support\Collection {#1178 ▼
#items: array:10 [▼
0 => array:18 [▶]
1 => array:18 [▶]
2 => array:18 [▶]
3 => array:18 [▶]
4 => array:17 [▶]
5 => App\Models\Post {#1760 ▶}
6 => App\Models\Post {#1758 ▶}
7 => App\Models\Post {#1757 ▶}
8 => App\Models\Post {#1756 ▶}
9 => App\Models\Post {#1755 ▶}
]
#escapeWhenCastingToString: false
}
what I have tried so far:
1- instead of pushing all the items I iterate through each item and then push it to the collection:
foreach($posts->items() as $item){
$this->posts->push($item);
}
2- instead of using collection using array:
public $posts = [];
foreach($posts->items() as $item){
array_push($this->posts, $item);
}
and others that I don't remember.
the strange thing is they are all returning the same thing for the first call and the second call, I have debugged the $posts variable for the first call and the second call they both return collections without issues.
the change happens only when pushing for the second time.
and the blade of course is like that:
#foreach($posts as $post)
<livewire:post-card :post="$post" :loop_index="$loop->index" wire:key="{{$loop->index}}"/>
#endforeach
#if($hasMorePages)
<div
x-data="{
init () {
let observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
#this.call('loadPosts')
}
})
}, {
root: null
});
observer.observe(this.$el);
}
}"
class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 mt-4"
>
#foreach(range(1, 4) as $x)
#include('components.skeleton')
#endforeach
</div>
#endif

Laravel get only users which they have specific Role using RelationShip

In my Laravel implemented web application i have Users and Roles tables with models which they are ManyToMany.
i want to get all users which they have specific role, for example:
$users = User::with(['roles' => function ($query) {
$query->whereLabel('is-portal-manager');
}])->get();
unfortunately this code return all users and compare role label and return that in relation ship array
Illuminate\Database\Eloquent\Collection {#2069 ▼
#items: array:3 [▼
0 => App\User {#2076 ▼
...
#relations: array:4 [▼
"properties" => null
"child" => Illuminate\Database\Eloquent\Collection {#2073 ▶}
"parent" => null
"roles" => Illuminate\Database\Eloquent\Collection {#2087 ▼
#items: array:1 [▼
0 => App\Entities\Role {#2074 ▼
...
#attributes: array:5 [▼
"id" => 1
...
"label" => "is-portal-manager"
...
]
...
}
]
}
]
...
}
1 => App\User {#2077 ▼
...
#relations: array:4 [▼
"roles" => Illuminate\Database\Eloquent\Collection {#2093 ▼
#items: []
}
]
...
}
]
}
as you can see in above output second user doesn't have any role and #items: [] is empty. i don't need to have this output structure and i want to have only which users have specific role, not all users with empty roles. second item of this collection shouldn't this output
Make your relationship conditional by using Laravel's whereHas:
$users = User::whereHas('roles', function ($query) {
$query->whereLabel('is-portal-manager');
})->with('roles')->get();
Official documentations for further reading on Eloquent Relationship Existence methods whereHas, has, doesntHave and whereDoesntHave

Laravel working with nested collection delete nested branch

unable to delete all nested branches where "product" => null
Have a query
$cartWhere['user_id'] = $user_id;
$cartWhere['site_id'] =$currentSite->id;
$item = Cart::select('product_id','quantity')->with(['product' => function($product) use ($search){
$product->join('product_translations', 'products.id', '=', 'product_translations.product_id');
$product->where( 'product_translations.name', 'LIKE','%'.$search.'%');
},'product.manufacturer:id,name'])
->where($cartWhere)->get();
then I receive collection that has all filtered carts but some of these carts have a relation with product => null
1 => App\Models\Cart {#736 ▼
...
#original: array:2 [▼
"product_id" => 1
"quantity" => 2
]
...
#relations: array:1 [▼
"product" => App\Models\Product {#785 ▶}
]
}
2 => App\Models\Cart {#736 ▼
...
#original: array:2 [▼
"product_id" => 2
"quantity" => 2
]
...
#relations: array:1 [▼
"product" => null
]
}
sorry for my English
You can filter them from the results using the collections filter() method
$item = Cart::select('product_id','quantity')
->with(['product' => function($product) use ($search) {
$product->join('product_translations', 'products.id', '=', 'product_translations.product_id');
$product->where( 'product_translations.name', 'LIKE','%'.$search.'%');
}, 'product.manufacturer:id,name'])
->where($cartWhere)
->get()
->filter(function ($cart) {
return $cart->product !== null;
});
Along with with you should use whereHas. So the result will not include any relation with null value. So this way you can also avoid the extra filter method used.

Merge 2 Collections or Arrays by Key

I'm trying my best to prevent from looping over a collection to get more data that I need for a data table. I need a way to combine these 2 collections or arrays.
I have 2 functions inside a model that calls an API that gives me an array of data that I convert into a collection.
Orders()
public function orders(){
$orders_array = $this->api()->request('orders.json');
return collect($orders_array);
}
Returns:
Collection {#274 ▼
#items: array:2 [▼
0 => {#282 ▼
+"id": 628602961977
}
1 => {#270 ▶}
order_meta()
public function order_meta($order_id){
$order_metafields_array = $this->api()->request('meta.json');
return collect($order_metafields_array);
}
Returns:
Collection
#items: [
+"name": "meta_data"
]
What I am wanting to do is inject the order_meta() into each order using the order ID.
I guess I am wanting to do something like this:
public function orders_detailed(){
$orders = $this->orders();
$keyed = $orders->mapWithKeys(function ($item) {
return [$item['meta'] => $this->order_metafields($item['id'])];
});
}
In hopes it will return:
Collection {#274 ▼
#items: array:2 [▼
0 => {#282 ▼
+"id": 628602961977
+meta => {
+"name":"meta_data"
}
}
1 => {#270 ▶}
Is something like this possible? I'm also open to merging it in as an array first, then creating a collection.

Laravel join with sub query

I have two models User and Address, one user can have many addresses but I want to fetch only which is primary(where primary=true)
this is what I have tried, and I am getting multiple users in the list
$users = User::join('addresses', 'addresses.user_id', 'users.id')
->select('name','phone_number','address','landmark','city','state','pincode','users.status')
->get();
I tried this too but I am not getting any data
$users = User::join('addresses', 'addresses.user_id', 'users.id')
->where('addresses.primary', true)
->select('name','phone_number','address','landmark','city','state','pincode','users.status')
->get();
thank you
Collection {#375 ▼
#items: array:8 [▼
0 => User {#376 ▶}
1 => User {#377 ▼
#attributes: array:9 [▼
"name" => "Colin Sushanth"
"phone_number" => "9987217545"
"address" => "2nd Cross 3rd street, y this is my address"
"landmark" => "Near the address"
"city" => "Mangalore"
"state" => "Karnataka"
"pincode" => 575002
"status" => "valid"
"primary" => "true"
]
}
2 => User {#378 ▶}
]
}
You should try this:
$users = User::join('addresses', 'addresses.user_id', 'users.id')
->where('addresses.primary','=',1)
->select('name','phone_number','address','landmark','city','state','pincode','users.status')
->get();
Or:
$users = User::join('addresses', 'addresses.user_id', 'users.id')
->where('addresses.primary','=',true)
->select('name','phone_number','address','landmark','city','state','pincode','users.status')
->get();
Updated answer
Blade file:
#if(isset($users))
#foreach($users as $user)
#if($user->primary === 'true')
//Your code
#endif
#endforeach
#endif
I would do it using eloquent models
So you would u do
User::whereHas('primary')->pluck('name','phone_number','address','landmark','city','state','pincode','users.status')
and your model will look like this
public function addresses()
{
return $this->belongsTo('App\Models\Address');
}
public function primary()
{
return $this->addresses->primary == true;
}

Categories