How to get item from laravel eloquent collection by conditions? - laravel

I use laravel eloquent to get files of post that give me a collection
but I want an item from that collection by condition.
For example from that collection I want an item that type = 'product'.
I am using foreach and check every item that have my condition and return it, but isn't any better way?
I tested collection method like contain but it return null.
Files item have type filed that value is 'product' or 'blog'.
My code:
$post= Post::where('slug' , $slug)->first();
$cover = $post->files->contains(['type' , '=' , 'product']);

Use the collection filter method.
$cover = $post->files->filter(function($file) {
// show only the items that match this condition
return collect(['product', 'blog'])->contains($file->type);
});

The filter method filters the collection using the given callback, keeping only those items that pass a given truth test:
$filtered = $post->files->filter(function ($value, $key) {
return $value->type == 'product';
});
$filtered->all();
Collections - filter()

Related

How to get a value from database on Laravel Controller?

I have this code where I want to select certain values from the table. Counter is my Model.
$today = '2022-03-16';
$tanggal = Counter::select('tanggal')->where('api', 'Agenda')->where('tanggal', $today)->get();
But if i dd($tanggal->tanggal), it returns with an error Property [tanggal] does not exist on this collection instance.
How to get a value from 'tanggal' attribute?
You are using get() method. it will return collection. You can not access any field directly from collection.
You have to need use freach loop to access single property.
$today = '2022-03-16';
$tanggals = Counter::select('tanggal')
->where('api', 'Agenda')->where('tanggal', $today)->get();
forech( $tanggals as $tanggal)
{
dump($tanggal->tanggal);
}
You are trying to access a model property from a Collection, this can be fixed like so:
$tanggal = Counter::where(['api' => 'Agenda', 'tanggal' => $today])->first(['tanggal']);
get() returns a Collection, first() returns a Model.
You can use the collection but then use its .each() method and give it a lambda instead to process each value.
$collection = Counter::select('tanggal')->where('api', 'Agenda')->where('tanggal', $today)->get();
$collection->each(function ($item, $key) {
//write now your code here can do $item->tanggal
});

Laravel firstOrCreate without Eloquent

Eloquent has a firstOrCreate method which gets a model based on a condition, or creates it if it doesn't exist.
Is there any equivalent method in Laravel's query builder (i.e. NOT in Eloquent)? For example:
$row = DB::table('users')->where('user_id', 5)->firstOrCreate('name' => 'Peter', 'last_name' => 'Pan');
That would try to get a row from users with 'user_id'==5. If it doesn't exist, it would insert a row with that id number, plus the other mentioned fields.
EDIT: I'm not trying to apply my question with users. I used users as an example to make as clear as possible what I'm looking for.
updateOrInsert function with empty values give me the result like firstOrCreate
Nope, Laravel firstOrCreate is function, that says next:
public function firstOrCreate(array $attributes, array $values = [])
{
if (! is_null($instance = $this->where($attributes)->first())) {
return $instance;
}
return tap($this->newModelInstance($attributes + $values), function ($instance) {
$instance->save();
});
}
But you can add it with query micro:
DB::query()->macro('firstOrCreate', function (array $attributes, array $values = [])
{
if ($record = $this->first()) {
// return model instance
}
// create model instance
});
So than you will be able to call it same way you do with Eloquent.
$record= DB::table('records')->where('alias', $alias)->firstOrFail();
Yeah of course! Just use normal SQL and ->selectRaw( your conditions ) and look for if there is a entry where your specifications are.
https://laravel.com/docs/5.7/queries#raw-expressions

Pagination not working on multiple querying

I have a multiple query, but for some reason the pagination is not working, i dont no if the ussue is the way i construct it.
My code:
$query = \DB::table('products');
if ($request->has('type') && $type) {
$query->where('product_type_id', $type);
}
if ($request->has('material') && $material) {
$query->join("product_attribute_materials","products.id","=","product_attribute_materials.product_id")
->whereIn("product_attribute_materials.product_material_id", $material);
}
if($request->has('businessarea') && $area){
$query->join("product_attribute_businessareas","products.id","=","product_attribute_businessareas.product_id")
->whereIn("product_attribute_businessareas.product_businessarea_id", $area);
}
$products = $query->paginate(15);
You need to append the query string to the pagination using append() on $products
Passing array with the keys that you want to pass the query string, will return its value on the next request.
To confirm this, chick the href value from your browser before clicking on page number 2.
$products = $query->paginate(15)->appends(['type', 'material', 'businessarea']);

Laravel Eloquent model with counter

I'm trying to count all the files within a category, and I have these two relationships:
public function files() {
return $this->hasMany('App\File', 'category_id','category_id');
}
public function fileCount() {
return $this->files()->selectRaw("category_id, count(*) AS count")
->groupBy('category_id');
}
This gives me a collection of items, where the count attribute could be accessed like this:
$my_category = Category::where("category_id", category_id)->with('fileCount')->get();
$file_counter = $my_category->first()->fileCount->first()->count;
Is it possible to directly attach the count attribute to the $my_category variable? Any suggestions will be appreciated.
You can use withCount() method:
$my_category = Category::where("category_id", category_id)
->withCount(['files' => function($q) {
$q->groupBy('category_id');
})
->get();
This will put files_count attribute into results.
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models

filtering a paginated eloquent collection

I am trying to filter a paginated eloquent collection, but whenever I use any of the collection methods, I lose the pagination.
$models = User::orderBy('first_name','asc')->paginate(20);
$models = $models->each(function($model) use ($filters) {
if(!is_null($filters['type'])) {
if($model->type == $filters['type'])
return $model;
}
if(!is_null($filters['state_id'])) {
if($model->profile->state_id == $filters['state_id'])
return $model;
}
if(!is_null($filters['city_id'])) {
if($model->profile->city_id == $filters['city_id'])
return $model;
}
});
return $models;
I am working with Laravel 4.2, is there any way to persist the pagination?
An answer to the titular question, which is possible in Laravel 5.2+:
How to filter the underlying collection of a Paginator without losing the Paginator object?
You can eject, modify and inject the collection as follows:
$someFilter = 5;
$collection = $paginator->getCollection();
$filteredCollection = $collection->filter(function($model) use ($someFilter) {
return $model->id == $someFilter;
});
$paginator->setCollection($filteredCollection);
The Paginator is built on an underlying collection, but indeed when you use any of the inherited Collection methods they return the underlying collection and not the full Paginator object: collection methods return collections for chaining together collection calls.
Expanding on mininoz's answer with your specific case:
//Start with creating your object, which will be used to query the database
$queryUser = User::query();
//Add sorting
$queryUser->orderBy('first_name','asc');
//Add Conditions
if(!is_null($filters['type'])) {
$queryUser->where('type','=',$filters['type']);
}
if(!is_null($filters['state_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('state_id','=',$filters['state_id']);
});
}
if(!is_null($filters['city_id'])) {
$queryUser->whereHas('profile',function($q) use ($filters){
return $q->where('city_id','=',$filters['city_id']);
});
}
//Fetch list of results
$result = $queryUser->paginate(20);
By applying the proper conditions to your SQL query, you are limiting the amount of information that comes back to your PHP script, and hence speeding up the process.
Source: http://laravel.com/docs/4.2/eloquent#querying-relations
I have a scenario that requires that I filter on the collection, I cannot rely on the Query Builder.
My solution was to instantiate my own Paginator instance:
$records = Model::where(...)->get()->filter(...);
$page = Paginator::resolveCurrentPage() ?: 1;
$perPage = 30;
$records = new LengthAwarePaginator(
$records->forPage($page, $perPage), $records->count(), $perPage, $page, ['path' => Paginator::resolveCurrentPath()]
);
return view('index', compact('records'));
Then in my blade template:
{{ $records->links() }}
paginate() is function of Builder. If you already have Collection object then it does not have the paginate() function thus you cannot have it back easily.
One way to resolve is to have different builder where you build query so you do not need to filter it later. Eloquent query builder is quite powerful, maybe you can pull it off.
Other option is to build your own custom paginator yourself.
You can do some query on your model before do paginate.
I would like to give you some idea. I will get all users by type, sort them and do paginate at the end. The code will look like this.
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
source: http://laravel.com/docs/4.2/pagination#usage
This was suitable for me;
$users = User::where('type', $filters['type'])->orderBy('first_name','asc')->paginate(20);
if($users->count() < 1){
return redirec($users->url(1));
}
A workaround when mixing search parameters and pagination, since default pagination won't keep the search parameters, using GET:
$urlSinPaginado = url()->full();
$pos=strrpos(url()->full(), 'page=');
if ($pos) {
$urlSinPaginado = substr(url()->full(), 0, $pos-1);
}
[...]
->paginate(5)
->withPath($urlSinPaginado);
sample generated pagination link: http://myhost/context/list?filtro_1=5&filtro_2=&filtro_3=&filtro_4=&filtro_5=&filtro_6&page=8
You can simply use this format in views
{!! str_replace('/?', '?', $data->appends(Input::except('page'))->render()) !!}
For newer versions of Laravel, you can use this:
$data->paginate(15)->withQueryString();

Resources