i am filtering data using collections. But i need to use like method. I had tried to write like this : ('name', 'LIKE', '%value%') but it did not work.
Here is my method :
protected function filterData(Collection $collection, $transformer) {
foreach (request()->query() as $query => $value) {
$attribute = $transformer::originalAttribute($query);
if (isset($attribute, $value)) {
$collection = $collection->where($attribute, $value);
}
}
return $collection;
}
The 1st question is whether you really know what you are doing. If you take data from database and then filter it just to take some elements it's definitely not the best way because you can take from database for example 100000 records just to finally have only 2 elements and it will kill your application performance.
But assuming you really want to filter using Support collection, there is no where together with LIKE because you are just filtering an array. If you want to use something similar to like instead of:
$collection = $collection->where('name', $value);
you can use:
$collection = $collection->reject(function($element) use ($value) {
return mb_strpos($element->name, $value) === false;
});
depending on what you really have in collection instead of $element->name you might need to use $element['name']
Related
Is there a way to pass multiple where conditions into the Laravel's collection? I mean something like:
$filters = [['col1', '=', 'val1'], ['col2', '=', 'val2']];
$found = $collection->where($filters)->first();
I know it works with the eloquent query builder, but not quite with the collections. I know I can chain multiple ->where statements (even within the foreach loop) but I need to have the original collection object, and cloning it works, but isn't so fast.
$localCollectionObject = $localCollection->first(function ($value, $key) use ($remoteCollectionObject, $compareColumnNames) {
foreach ($compareColumnNames as $compareColumnName) {
if ($value->{$compareColumnName} != $remoteCollectionObject[$compareColumnName]) {
return false;
}
}
return true;
});
This works fine also, but is even slower than clone $localCollection.
Or maybe I can "reset" where statements somehow?
I'm searching it within foreach loop with different conditions and that's a problem.
You can always use your own closure with filter:
$collection->filter(function ($item) {
return $item->col1 == 'val1' && $item->col2 == 'val2';
});
I want to use 99% of my code to do two queries.
When accessing api.com/jobs it will display all my jobs, but acessing api.com/jobs/65 will only display that entire job.
My improvement is to have an if before the foreach, to detect whether it is one single id or to list all.
My code looks like this:
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs = $Repo->find($param);
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key=>$job){
...
}
The problem is: the findAll() is doing great but I can't enter the foreach with the find() only;
Is any way to do what I want?
If you must use this approach (I would prefer to separate concerns) put the result into an array;
edited
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs[] = $Repo->find($param);
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key => $job){
// there is a risk that whats in $jobs might not be what you expect. So check.
if(!$job instanceOf Jobs::class) {
continue;
}
}
alternatively, use findBy as itll return a Collection object which will iterate.
$Repo->findBy(['column' => 'value']);
I would suggest having different pieces of code to deal with the two cases but, if you would rather keep them together:
$Repo = $entityManager->getRepository('App\Models\Entity\Jobs');
if ($param){
$jobs = array($Repo->find($param));
} else{
$jobs = $Repo->findAll();
}
foreach ($jobs as $j_key=>$job){
...
}
This will create an array with a single element in the case a request is made for a single job, and a request for multiple jobs will return all the matching jobs.
I have a collection that I am trying to filter on created_at year. Laravel 5.3. I am trying to use whereYear inside my filter but it's sqwaking that the method is undefined. How do I define it? Or is there a better way?
$datas = Campaign::all();
if($request->year) {
$value = $request->year;
$datas = $datas->filter(function($data) use ($value) {
return $data->created_at->whereYear($value);
});
}
I would do this more compact:
So in your Controller you can do:
$datas = ($request->year)
? Campaign::inYear($request->year)->get()
: Campaign::all();
The advantage is, that you get only the record that you need from the DB, and don't have to do any kind of filter on a Collection. This will also increaser your performance a little bit.
In your Campaign Model you add on inYear scope that is than reusable:
use Carbon\Carbon;
class Campaign extends Model
{
public function scopeInYear($query, $year)
{
return $query->whereBetween('created_at', [
Carbon::create($year)->startOfYear(),
Carbon::create($year)->endOfYear(),
]);
}
}
In laravel created_at is an instance of Carbon so :
$datas = Campaign::all();
if($request->year) {
$value = $request->year;
$datas = $datas->filter(function($data) use ($value) {
return $data->created_at->year == $value;
});
}
whereYear(...) is shorthand for where('year', ...) in Laravel, but you want to be searching against created_at, so that's not going to work. Something like this should do the trick, and reduce the number of records you have to fetch from the DB as well:
// better to let the database handle the search
// than fetching all records and filtering the collection
$query = Campaign::query();
if($request->year) {
// be sure to validate $request->year somewhere, for obvious reasons...
$query->whereBetween('created_at', $request->year . '-01-01 00:00:00', $request->year . '-12-31 23:59:59')
}
return $query->get();
I am using Laravel and I want to retrieve the data using Eloquent.
My controller:
public function getquesdet(){
$id = Request::input('id');
$question = Question::where('q_id','=',$id)->with('qtags.tags')->with('comments')->with('answers')
->first();
$i=0;
$tagnames[]=0;
foreach ($question['qtags'] as $value) {
$tagnames[$i] = $value['tags']['tag'];
$i++;
}
$j=0;
$comments[]=0;
foreach ($question['comments'] as $value) {
$comments[$j] = $value['comment'];
$j++;
}
$k=0;
$answers[]=0;
foreach ($question['answers'] as $value) {
$answers[$k] = $value['answer'];
$k++;
}
return array('question'=>$question['title'],'body'=>$question['body'],'tags'=>$tagnames,'comments'=>$comments,'answer'=>$answers);
}
As you can see, I feel that the usage of foreach loops is not efficient. Using for loops might take more time. All I want is to know that if there is any efficient workaround to this.
The $question is returned as :
I think that looks like an efficient solution. Three for loops after each other gives complexity O(3n) -> O(n) which is good and should be fast. If you have very large collections of comments, tagnames and questions I would try to solve the problem at SQL level which should be even faster, but for small resultsets this should be good enough.
Big O notation
If you want selected columns from the table then you can use select() method as:
$question = Question::where('q_id','=',$id)
->select('title', 'body')
->with([
'qtags' => function($q) {
$q->select('tag');
},
'qtags.tags' => function($q) {
$q->select('comment');
},
'answers' => function($q) {
$q->select('answer');
}
])
->first();
Then you can return it as an array:
return $question->toArray();
Docs
I have a questions table and a tags table. I want to fetch all questions from tags of a given question. So, for example, I may have the tags "Travel," "Trains" and "Culture" attached to a given question. I want to be able to fetch all questions for those three tags. The tricky, so it seems, is that questions and tags relationship is a many-to-many defined in Eloquent as belongsToMany.
I thought about trying to merge the questions Collections as below:
foreach ($question->tags as $tag) {
if (!isset($related)) {
$related = $tag->questions;
} else {
$related->merge($tag->questions);
}
}
It doesn't seem to work though. Doesn't seem to merge anything. Am I attempting this correctly? Also, is there perhaps a better way to fetch a row of rows in a many-to-many relationship in Eloquent?
The merge method returns the merged collection, it doesn't mutate the original collection, thus you need to do the following
$original = new Collection(['foo']);
$latest = new Collection(['bar']);
$merged = $original->merge($latest); // Contains foo and bar.
Applying the example to your code
$related = new Collection();
foreach ($question->tags as $tag)
{
$related = $related->merge($tag->questions);
}
The merge() method on the Collection does not modify the collection on which it was called. It returns a new collection with the new data merged in. You would need:
$related = $related->merge($tag->questions);
However, I think you're tackling the problem from the wrong angle.
Since you're looking for questions that meet a certain criteria, it would probably be easier to query in that manner. The has() and whereHas() methods are used to generate a query based on the existence of a related record.
If you were just looking for questions that have any tag, you would use the has() method. Since you're looking for questions with a specific tag, you would use the whereHas() to add the condition.
So, if you want all the questions that have at least one tag with either 'Travel', 'Trains', or 'Culture', your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->whereIn('name', ['Travel', 'Trains', 'Culture']);
})->get();
If you wanted all questions that had all three of those tags, your query would look like:
$questions = Question::whereHas('tags', function($q) {
$q->where('name', 'Travel');
})->whereHas('tags', function($q) {
$q->where('name', 'Trains');
})->whereHas('tags', function($q) {
$q->where('name', 'Culture');
})->get();
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->merge($associates);
Merge two different eloquent collections into one and some objects happen to have the same id, one will overwrite the other. Use push() method instead or rethink your approach to the problem to avoid that.
Refer to web
Creating a new base collection for each eloquent collection the merge works for me.
$foo = collect(Foo::all());
$bar = collect(Bar::all());
$merged = $foo->merge($bar);
In this case don't have conflits by its primary keys.
I have faced some issue by using merge. So I used concat. You can used it like below.
$users = User::all();
$associates = Associate::all();
$userAndAssociate = $users->concat($associates);
All do not work for me on eloquent collections, laravel eloquent collections use the key from the items I think which causes merging issues, you need to get the first collection back as an array, put that into a fresh collection and then push the others into the new collection;
public function getFixturesAttribute()
{
$fixtures = collect( $this->homeFixtures->all() );
$this->awayFixtures->each( function( $fixture ) use ( $fixtures ) {
$fixtures->push( $fixture );
});
return $fixtures;
}
I'm sorry about that, but since PHP 7.4 you're available to do like this (better use merge).
$foo = Foo::all();
$bar = Bar::all();
/** $foo will contain $foo + $bar */
$foo->push(...$bar);
I would like to add that, i found that the concat method does not seem to override based on ID, while the merge method does. concat seems to work for me, while merge caused issues.