I am trying to query a products table, and want it to return a collection if a relation exists.
Iteration 1 below queries all rows in the products table, and lazy loads the metals table if $name matches. This is wrong.
My Route:
Route::group(array('prefix' => '{api}/v1'), function()
{
Route::controller('products', 'Api\V1\ProductController');
});
My Controller:
public function getFilter($metal = null) {
$products = $this->product;
if ($metal) {
$products->with('metal', function($query, $metal) {
$query->where('name', $metal);
});
}
return Response::api($products->get());
}
I want only $products to display if metal.name = $metal. e.g. something like:
$this->products->where('metal.name', $metal)->get;
Solution using part of Glad To Help's answer:
This provides an alternative approach 2, without the need for joins.
http://paste.laravel.com/WC4
Unfortunately you cannot do this with one swipe in Eloquent yet.
BUT, there is a way by using the inverse relation, like this:
public function getFilter($metal = null)
{
// filter the metals first
$metals = Metal::with('products')->where('name', '=' , $metal)->get();
$products = array();
foreach($metals as $metal)
{
// collect the products from the filtered metals
$products = array_merge($products, $metal->products->toArray() );
}
return $products;
}
If this is not elegant solution for you, you will either have to use Fluent to construct the query and join the products x metals table manually or pre-join them by overriding the newQuery() method.
1) alternative approach one.
public function getFilter($metal = null) {
return DB::table('products')->join('metal', 'products.id', '=' , 'metal.product_id')
->where('metal.name', $name)
->select(array('products.*'));
}
2) alternative approach two
class Product extends Eloquent{
public function newQuery($excludeDeleted = true){
return parent::newQuery()->join('metal','id','=','metal.product_id');
}
}
Related
This is my Controller
$categoryIds = Category::select('id')->with('childrenRecursive')->where('id', 1)->get();
Ad::whereIn('category_id', $categoryIds)->get();
This is my model
public function parent() {
return $this->belongsTo(Category::class, 'parent_id');
}
public function childs() {
return $this->hasMany(Category::class, 'parent_id');
}
public function Ads() {
return $this->hasManyThrough(Ad::class, Category::class, 'parent_id', 'category_id', 'id');
}
How get all childern categories ides
I solved this problem with this solution
My Controller
public function index()
{
$parent = Category::with('descendants')->find(1);
$descendants = $this->traverseTree($parent, collect([1]));
$ads = Ad::whereIn('category_id',$descendants)->get();
return response($ads);
}
protected function traverseTree($subtree, $des)
{
$descendants = $des;
if ($subtree->descendants->count() > 0) {
foreach ($subtree->descendants as $descendant) {
$descendants->push($descendant);
$this->traverseTree($descendant, $descendants);
}
}
return $descendants;
}
I'd do it with Laravel's Subqueries approach.
$parentId = 4;
Ad::whereIn('category_id', function($q) use ($parentId) {
$q->select('id')
->from('categories')
->where('parent_id', $parentId);
});
If you want to add the parent model, you can chain with():
Ads::whereIn('category_id', function($q) use ($parentId) {
$q->select('id')
->from('categories')
->where('parent_id', $parentId);
})
->with('category.parent')
->get();
Your code chunks are not clear so you may need to tweak my code example.
If I understand your question properly you need to get ads corresponding to id's of all related records also, for a given category record.
$category = Category::with('childs:id,parent_id')
->where('id', 1)
->firstOrFail();
$categoryIds = collect([$category->parent_id, $category->id]);
$category->childs->map(fn($child) => $categoryIds->push($child->id));
$ads = Ads::whereIn('category_id', $categoryIds->filter()->all())
// Can eager load the product(s) if needed
//->with('products')
->get();
I'm new to Laravel and I can write simple eloquent queries but have no idea how to convert this query to eloquent. Can anyone give any idea, is it possible to convert this to eloquent or I have to write raw query?
"Select categories.id, categories.name, Sum(likes.liked) as liked
FROM categories, likes
WHERE likes.material_id IN (SELECT category_material.material_id
FROM category_material
WHERE category_material.category_id = categories.id)
GROUP BY categories.id";
Here my Models
class Material extends Model
{
public function categories(){
return $this->belongsToMany(Category::class,'category_material');
}
public function likes(){
return $this->hasMany(Like::class);
}
////////////////////////////////////////////////////////////
class Like extends Model
{
protected $table = 'likes';
public function user(){
return $this->belongsTo(User::class);
}
public function material(){
return $this->belongsTo(Material::class);
}
//////////////////////////////////////////////////////
class Category extends Model
{
public function materials(){
return $this->belongsToMany(Material::class,'category_material');
}
You can define a likes relationship in your Category model like so:
public function likes()
{
return $this->belongsToMany(Like::class, 'category_material', 'category_id', 'material_id', 'id', 'material_id');
}
Then to achieve what you're after with Eloquent you can use a mixture of has() and withCount, however, we're going to modify the withCount call to return a sum() instead:
$catrgories = Category::has('likes')->withCount([
'likes as liked' => function ($query) {
$query->select(DB::raw('SUM(likes.liked)'));
},
])->get();
If you're wanting to return categories that don't have any likes you can remove the has() method, and introduce the COALESCE() function to your raw query:
$catrgories = Category::withCount([
'likes as liked' => function ($query) {
$query->select(DB::raw('COALESCE(SUM(likes.liked), 0)'));
},
])->get();
Alternatively, you could simply load the necessary relationships and then use that fact that Eloquent returns collection to get the value after you've retrieved the results from the database:
$categories = Category::with('materials.likes')->get()->map(function ($item) {
$item->setAttribute('liked', $item->materials->map(function ($item) {
return $item->likes->map->sum('liked')->sum();
})->first());
$item->unsetRelation('materials');
return $item;
});
This would mean that you don't have to add the custom relationship.
How do I update all rows in a table (all IDs) at once, with a database query in Laravel? My current controller code is as follows:
public function updateSchedule(Request $request, $id)
{
$timein = $request->input('timeIn');
$timeout = $request->input('timeOut');
DB::table('schedules')
->where('id', 1)
->update(['time_in' => $timein, 'time_out' => $timeout]);
}
To update the whole table (the WHOLE TABLE), you would do the following (removing the condition that you only want to update matches where id = 1):
public function updateSchedule(Request $request, $id)
{
$timein = $request->input('timeIn');
$timeout = $request->input('timeOut');
DB::table('schedules')
->update(['time_in' => $timein, 'time_out' => $timeout]);
}
If you want to see something afterwards, you either need to return that value, or a view, or a redirect to the prior page.
For example, the following (although it's not good practice to use back() as you don't know where they came from. It's better to go to a specific route / url).
public function updateSchedule(Request $request, $id)
{
$timein = $request->input('timeIn');
$timeout = $request->input('timeOut');
DB::table('schedules')
->update(['time_in' => $timein, 'time_out' => $timeout]);
return back();
}
I'm trying to learn cakephp 3 and the ORM functions, wicht is great so far. But know I'm comming to a point on wich I'm not certain how I can aproach it in the best way, so I was hoping that somebody can tell what is the best way.
I'm using the query builder to load one or more products. In the data that's loaded I have one field called price. This is the main price of the product, but there can be an optional discount for an user. I know wich user is logged in to the system so I have an variabele witch contains his discount, for example 1.20 (=20%).
After the query has been fired I could do an foreach and recalculate the price before sending is to the view, but because of the query builder function I suspect that I can do it there before I fired the query.
Below an example where an search is done on input name. The field price is now standard, but should be recalculated with the discount.This could be an foreacht example:
$price = round((($price/$User->discount)-$shipping),2);
SearchController:
public function search()
{
if ($this->request->is('post')) {
//$this->request->data);
$options = array();
$options['query'] = $this->request->data['Search']['query'];
$products = TableRegistry::get('Products');
$query = $products->find('search', $options);
$number = $query->count();
$products = $query->All();
$this->set(compact('products'));
$this->set('number_of_results',$number);
}
The ProductsTable:
public function findSearch(Query $query, array $options)
{
$query
->where([
'name LIKE' => '%'.$options['query'].'%',
"active" => 1
])
->contain(['ProductsImages'])
->select(['id','name','price','delivery_time','ProductsImages.image_url'])
;
return $query;
}
Is there an way to implement this recalculation in the query builder? I tried to find some info on the web but there isn't much information yet about the ORM options. I was thinking about maybe ->combine. Hopefully someone wants to put me in the right direction.
Thanks in forward.
Changed the controller function to:
public function search()
{
if ($this->request->is('post')) {
$options = array();
$options['query'] = $this->request->data['Search']['query'];
$discount = 2;
$products = TableRegistry::get('Products');
$query = $products
->find('search', $options);
$query->formatResults(function (\Cake\Datasource\ResultSetInterface $products) {
return $products->map(function ($row) {
$row['price'] = ($row['price'] + 10);
return $row;
});
});
$number = $query->count();
$products = $query->All();
$this->set(compact('products'));
$this->set('number_of_results',$number);
}
}
I am working with options, to add some additional info like image. and I saved this data to my own table with option_type_id and option_id. now on frontend I would like to join my own table data to default options. so these options come with image info.
$_option->getValues()
this function returns option data, now I have to reach the implementation of this function where it generate the query so I could add join to retrieve my own data with.
I dont see a clean way to do this.
Here is a dirty way:
RewriteMage_Catalog_Model_Resource_Product_Option and add this function below.
Modify it with you join. however the join to you table would then be done for every product option. You will need to check for somekind of a flag and only add your join if this flag is set.
protected function _getLoadSelect($field, $value, $object)
{
$select = parent::_getLoadSelect($field, $value, $object);
if("do your check here"){
$select->join('your table')
}
return $select;
}
Here is what i got success from.
i overridden the resource collection of product
class MYC_COPSwatch_Model_Resource_Product_Option_Collection extends Mage_Catalog_Model_Resource_Product_Option_Collection{
public function addValuesToResult($storeId = null)
{
if ($storeId === null) {
$storeId = Mage::app()->getStore()->getId();
}
$optionIds = array();
foreach ($this as $option) {
$optionIds[] = $option->getId();
}
if (!empty($optionIds)) {
/** #var $values Mage_Catalog_Model_Option_Value_Collection */
$values = Mage::getModel('catalog/product_option_value')
->getCollection()
->addTitleToResult($storeId)
->addPriceToResult($storeId)
->addSwatchToResult($storeId) //USED Join in this function
->setOrder('sort_order', self::SORT_ORDER_ASC)
->setOrder('title', self::SORT_ORDER_ASC);
foreach ($values as $value) {
$optionId = $value->getOptionId();
if($this->getItemById($optionId)) {
$this->getItemById($optionId)->addValue($value);
$value->setOption($this->getItemById($optionId));
}
}
}
return $this;
}
might be save time for someone.