This is not really a problem. but i feel like this can be done in much cleaner way.
I'm returning 2 collections in one view and this is how i do it.
I'm wondering if this is "the right" way of doing it?
Thanks in advance you intelligent humans.
This is within my controller.
$projects = Project::with('client')->where('project_id','=',$id)->get();
foreach($projects as $project){
$project;
}
$clients = DB::table('clients')->select('client_name')->get();
return view('admin.projects.update')->with('project',$project)->with('clients', $clients);
You can try compact method. In the last line write,
return view('admin.projects.update',compact('project','clients'));
(P.S. remove "return $clients;". Otherwise it won't return anything.)
You do like this also
return view('admin.projects.update')->with([project' => $project,'clients' => $clients]);
And like this too
return view('admin.projects.update',[project' => $project,'clients' => $clients]);
Related
I want to delete all the votes where the flag "isOnly" is true which means the article is voted before the plenary session.
I have this code written, which deletes ALL the votes.
foreach($commision->articles as $article) {
$article->votes()->delete();
$article->update([
'isVoted' => false
]);
}
What is the right way to delete all the votes with the flag 'isOnly' == true
You can stack where methods with delete call
$article->votes()->where('isOnly', true)->delete();
One better solution would be to avoid the foreach all together so you run only one query
$articleIds = $commision->pluck('articles.id'); //if the articles are already loaded calling a collection method pluck()
$articleIds = $commision->articles()->pluck('id'); // if articles are not loaded calling a query builder method pluck()
Votes::whereHas('article', function($articleQueryBuilder) use($articleIds) {
$articleQueryBuilder->whereIn('id', $articleIds);
})->where('isOnly', true)->delete();
Article::whereIn('id', $articleIds)->update([
'isVoted' => false
]);
This will result in a faster processing of your delete() & update().
Here is one way to delete by condition
$article->votes()->get()->filter(function($item){
return $item->isOnly == true;
})->each(function($vote){$vote->delete();});
This statement will get all votes and apply filter funtion on votes which will give us votes which has isOnly == true rows. Then each function will delete returned votes.
This will help. :)
Im quite new to laravel and so far I realy enjoy eloquent and querybuilder, but as soon as the queries become more complex, my head starts to hurt... I have just finished 2 working examples after quite some time, maybe you guys can help me optimize it. I will first give the examples, then the (dis)advantages
So first the DB::select... which honestly, I think is more readable then the 2nd example.. or maybe I am just doing the builder thing wrong xD.
$shared_slugs = Magic::in_query($this->shared_slugs);
$items = DB::select("
SELECT * FROM f_admin_items
WHERE
active = 1
AND (
slug IN $shared_slugs
OR
:treshhold <= :urank AND id IN (SELECT admin_item_id FROM f_user_access WHERE user_id = :uid)
OR
:treshhold > :urank AND `rank` >= :urank
)
ORDER BY `order` ASC
", [
'treshhold' => $this->threshold_rank,
'urank' => $user_rank,
'uid' => $user_id
]);
Overall pretty effective with the named parameter binding. Basicly the menu items always have to be active, plus (1 OR 2 OR 3 ). For example dashboard is accepted as shared slug.. Otherwise there are some ranking checks.
Now I have been trying hard to do this with eloquent en query builder =/ I didn't think setting up a relation was not necessary because I only use this for the menu and a middleware. In this case the f_user_access table containing only admin_item_id and user_id, doesn't really function as a pivot and isn't used anywhere else.
$items =
$this->where( 'active', 1 )
->where( function ($query) {
$query->whereIn( 'slug', $this->shared_slugs )
->orWhere(function ($query) {
$query->whereRaw( $this->threshold_rank.' <= '.$this->user_rank )
->whereIn('id', function ($query) {
$query->select('admin_item_id')->from('f_user_access')->where('user_id', $this->user_id)->get();
});
})
->orWhere(function ($query) {
$query->whereRaw( $this->threshold_rank.' > '.$this->user_rank )
->where( 'rank', '>=', $this->user_rank );
});
})->orderBy( 'order', 'ASC' )->get();
What I like about this second one is the fact I can pass $shared_slugs as an array, I don't have to convert it into a string first. But apart from that I really hate the looks of this, where(function($query){}) etc etc... On top of that the $ids passed to this function, are not accessible in the where.functions, so I had to define them in the class first.
The first one I like because of the named binding and it doesn't read that bad to be :S, also the vars are accessible.. Downside, I have to call this function to convert the shared slugs into an array.
Is it really that bad to not use eloquent and querybuilder? at least in some cases... and what would you guys do to make the second example better? =/
UPDATE::
Due to the answers and feedback, I ditched the raw sql. The current function has 5 scopes in it, and a (small) model is made for the user_access.
$items =
$this->active() //Only active items AND... 1, 2 or 3
->where( function ($query) {
$query->shared_slug() // 1
->orWhere(function ($query) { // OR 2
$query->access_required()
->whereIn('id', UserAccess::get_access( $this->user_id ) );
})
->orWhere(function ($query) { // OR 3
$query->no_access_required()
->whereRaw( 'rank >= '.$this->user_rank );
});
})->ASC()->get();
The major benefit of using the query builder is it abstracts you away from the language used by your storage of choice, i.e. MySQL, Oracle, SQLite, etc. If you ever switch database types, you can be stuck with a lot of refactoring raw SQL. Believe me, it's not pretty when you start on a project and learn this is the case.
There are always caveats, however, which is precisely why Eloquent has the capability to work with raw statements, as well.
I guess I am breaking all the rules by deliberately making a duplicate question...
The other question has an accepted answer. It obviously solved the askers problem, but it did not answer the title question.
Let's start from the beginning - the first() method is implemented approximately like this:
foreach ($collection as $item)
return $item;
It is obviously more robust than taking $collection[0] or using other suggested methods.
There might be no item with index 0 or index 15 even if there are 20 items in the collection. To illustrate the problem, let's take this collection out of the docs:
$collection = collect([
['product_id' => 'prod-100', 'name' => 'desk'],
['product_id' => 'prod-200', 'name' => 'chair'],
]);
$keyed = $collection->keyBy('product_id');
Now, do we have any reliable (and preferably concise) way to access nth item of $keyed?
My own suggestion would be to do:
$nth = $keyed->take($n)->last();
But this will give the wrong item ($keyed->last()) whenever $n > $keyed->count(). How can we get the nth item if it exists and null if it doesn't just like first() behaves?
Edit
To clarify, let's consider this collection:
$col = collect([
2 => 'a',
5 => 'b',
6 => 'c',
7 => 'd']);
First item is $col->first(). How to get the second?
$col->nth(3) should return 'c' (or 'c' if 0-based, but that would be inconsistent with first()). $col[3] wouldn't work, it would just return an error.
$col->nth(7) should return null because there is no seventh item, there are only four of them. $col[7] wouldn't work, it would just return 'd'.
You could rephrase the question as "How to get nth item in the foreach order?" if it's more clear for some.
I guess faster and more memory-efficient way is to use slice() method:
$collection->slice($n, 1);
You can try it using values() function as:
$collection->values()->get($n);
Based on Alexey's answer, you can create a macro in AppServiceProvider (add it inside register method):
use Illuminate\Support\Collection;
Collection::macro('getNth', function ($n) {
return $this->slice($n, 1)->first();
});
and then, you can use this throughout your application:
$collection = ['apple', 'orange'];
$collection->getNth(0) // returns 'apple'
$collection->getNth(1) // returns 'orange'
$collection->getNth(2) // returns null
$collection->getNth(3) // returns null
you may use offsetGet since Collection class implements ArrayAccess
$lines->offsetGet($nth);
Maybe not the best option, but, you can get item from array inside collection
$collection->all()[0]
I'm trying to merge multiple objects (like Receipts, Reports, etc) with Collection->merge().
This is the code I used:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->merge($reports);
This is the result:
The above screenshot shows two elements, but the third element is missing because it has the same id (id: "1") as the first one. What I'm trying to achieve is to display all three of them as a collection.
EDIT:
I need the result to be objects (collection) because I also use the code on my view, where I check the class to determine what to display. Also, I use this function to sort the objects in the collection.
$collection->sort(function($a, $b)
{
$a = $a->created_at;
$b = $b->created_at;
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
});
I know that this is an old question, but I will still provide the answer just in case someone comes here from the search like I did.
If you try to merge two different eloquent collections into one and some objects happen to have the same id, one will overwrite the other. I dunno why it does that and if that's a bug or a feature - more research needed. To fix this just use push() method instead or rethink your approach to the problem to avoid that.
Example of a problem:
$cars = Car::all();
$bikes = Bike::all();
$vehicles = $cars->merge($bikes);
// if there is a car and a bike with the same id, one will overwrite the other
A possible solution:
$collection = collect();
$cars = Car::all();
$bikes = Bike::all();
foreach ($cars as $car)
$collection->push($car);
foreach ($bikes as $bike)
$collection->push($bike);
Source: https://medium.com/#tadaspaplauskas/quick-tip-laravel-eloquent-collections-merge-gotcha-moment-e2a56fc95889
I know i'm bumping a 4 years old thread but i came across this and none of the answers were what i was looking for; so, like #Tadas, i'll leave my answer for people who will come across this. After Looking at the laravel 5.5 documentation thoroughly i found that concat was the go-to method.
So, in the OP's case the correct solution would be:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->concat($reports);
This way every element in the Report collection will be appended to every element in the Receipts collection, event if some fields are identical.
Eventually you could shuffle it to get a more visual appealing result for e.g. a view:
$collection->shuffle();
Another way to go about it is to convert one of your collections to a base collection with toBase() method. You can find it in Illuminate\Support\Collection
Method definition:
/**
* Get a base Support collection instance from this collection.
*
* #return \Illuminate\Support\Collection
*/
public function toBase()
{
return new self($this);
}
Usage:
$receipts = Receipt::all();
$reports = Report::all();
$collection = $receipts->toBase()->merge($reports);
You could put all collections in an array and use this. Depends on what you want to do with the collection.
$list = array();
$list = array_merge($list, Receipt::all()->toArray());
$list = array_merge($list, Report::all()->toArray());
I'm having a problem where I can't get add joinField() to a product collection.. I have no clue why it doesn't work because it should be really simple or throw some errors at least. Needless to say, it is driving me nuts. I'm interested in looking at products and the total dollar amount in sales from them. This is what I have from a book called "Magento PHP Developer's Guide" and the Magento Wiki.
public function getProducts($categoryId) {
$productCollection = Mage::getModel('catalog/category')->load($categoryId)
->getProductCollection()
->joinField('o', 'sales_flat_order_item', array('o.row_total', 'o.product_id'), 'main_table.entity_id = o.product_id');
}
// die; when uncommented, this function WILL NOT die here
return $productCollection;
}
I'm getting the ->joinField() method right out of the book, but it doesn't grab any product. Strangely, the function doesn't even return anything because when the die line is uncommented, the function does not terminate there. Instead, the front-end will simply just skip this function without throwing any errors (that I can see at this time) and just doesn't display any blocks using this function. What am I missing here?
It works when I remove joinField() like below.
$productCollection = Mage::getModel('catalog/category')->load($categoryIds)
->getProductCollection();
UPDATE:
Further testing show that the following works. Note that if I use main_table instead of e, it does not work. If I look at the query generated from this, main_table is not replaced by the main table; instead, query contains the literal string "main_table".
$productCollection = Mage::getModel('catalog/category')->load($categoryIds)
->getSelect()
->join(array('o' => 'sales_flat_order_item'),
'e.entity_id = o.product_id',
'o.row_total'
);
While this doesn't.
$productCollection = Mage::getModel('catalog/category')->load($categoryIds)
->joinTable(
array('o' => 'sales_flat_order_item'),
'e.entity_id = o.product_id',
'o.row_total'
);
Maybe I don't see some simple mistake.. but I just don't see what's wrong.
public function getProducts($categoryId) {
$productCollection = Mage::getModel('catalog/category')->load($categoryId)
->getProductCollection()
->joinTable( // JoinTable makes more sense.
array('o' => 'sales/order_item'),
'main_table.entity_id = o.product_id'
array('row_total'),
)
;
return $productCollection;
}
It will probably return all fields in the catalog_product_entity table PLUS the row_total from the sales_order_item table. You might be able to use addAttributeToSelect('o.product_id') right before the join, just to clear the unwanted fields.