I made a system to calculate the total price of a basket, it works but I saw that there were duplicate requests, I would like to share the code with you to know if you would have done otherwise?
For the moment I have not found any other solution.
Userscontroller.php :
$carts =\Auth::user()->carts;
User.php (model) :
public function carts()
{
return $this->hasMany('App\Models\Cart')->with('product');
}
Cart.php (model) :
public function product()
{
return $this->belongsTo('App\Models\Product', 'product_id')->with('productCat');
}
public function getTotalPrice()
{
$price = 0;
foreach($this->user->carts as $carts)
{
$price += $carts->getPriceByQuantity();
}
return $price;
}
public function getPriceByQuantity()
{
$quantity = $this->quantity;
$price = ($this->product->product_total_price == '') ? $this->product->priceRender() : $this->product->product_total_price;
return $price * $quantity;
}
The problem is that i declare user->carts one time in userscontroller for the view, and one time in getTotalPrice. Before, i wanted to loop over $this instead of $this->user->carts, but the loop was repeated by the quantity number of a product.
If someone knows the best way to do this ? Thank you
Related
Good evening everyone,
I have some issues in my project. I'm trying to send mail to each collaborator who celebrates his birthday. The data are taken from my database. The problem is that: if I have for example 2 or 3 collaborators who celebrate their birthday in the same day, only the first in the list receive a mail. My question how send to all concerned collaborator a mail.
My code:
public function handle()
{
$collaborateurs = Card::listCard();
foreach ($collaborateurs as $collaborateur) {
return Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}
}
class CarteAnnif {
public static function listCard(){
$collaborateurs = Organigramme::whereMonth('date_de_naissance', now()->month)
->whereDay('date_de_naissance', now()->day)
->get();
$collaborateursConcernes = [];
foreach ($collaborateurs as $collaborateur) {
$date_de_naissance = Carbon::createFromFormat('d/m/Y', $collaborateur->date_de_naissance)->format('d-m');
$date_de_naissance = strtotime($date_de_naissance);
$today = date('d-m');
$today = strtotime($today);
if($date_de_naissance == $today ){
$collaborateursConcernes[] = $collaborateur;
}
}
return collect($collaborateursConcernes);
}
}
public $collaborateur;
public function __construct($collaborateur)
{
$this->collaborateur = $collaborateur;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$this->view('admin.emails.send_card_to_collaborateurs')
->subject("Joyeux anniversaire");
return $this->from('app#domain.com')->view('admin.emails.send_card_to_collaborateurs');
}
}
I hope I have been clear otherwise I remain available for any other additional information
Thanks in advance!
The return ends a function, so your function will stop after the first loop. Just delete the retun :
foreach ($collaborateurs as $collaborateur) {
Mail::to($collaborateur->adresse_email)->send(new SendEmail($collaborateur));
}
So I am making a Businesses web app with the filters feature. There are two filters that I have problem with: Order By and Attributes(Has following attributes) features. Which looks like this:
Order By
Highest Rated (radio button)
Most reviews (radio button)
Attributes
Accepts Credit Cards (checkbox)
Accepts Events (checkbox)
Alcohol (checkbox)
Delivery (checkbox)
Smoking (checkbox)
So when Order By option is clicked this function is executed. Where $term is value of order_by get request parameter.
BusinessFilter.php
public function orderby($term)
{
if ($term == 'reviews_count') {
return $this->builder
->leftJoin('reviews', 'businesses.id', '=', 'reviews.business_id')
->groupBy('businesses.id')
->selectRaw('businesses.*, COUNT(reviews.id) as reviews_count')
->orderByDesc('reviews_count');
} else if ($term == 'rating') {
return $this->builder
->leftJoin('reviews', 'businesses.id', '=', 'reviews.business_id')
->groupBy('businesses.id')
->selectRaw('businesses.*, AVG(reviews.rating) AS average')
->orderByDesc('average');
} else {
return $this->builder;
}
}
It works ok and the result is correct.
Now when Attribute have some check boxes this function is executed where $term is an array with set of ids.
BusinessFilter.php
public function attributes($term)
{
$attributes= json_decode($term);
if (count($attributes) == 0) {
return $this->builder;
}
return $this->builder
->select('businesses.*')
->join('business_attribute_value', 'businesses.id', '=', 'business_attribute_value.business_id')
->join('values', 'business_attribute_value.attribute_value_id', '=', 'values.id')
->whereIn('values.id', $attributes)
->groupBy('businesses.id')
->havingRaw('COUNT(*) = ?', [count($attributes)]);
}
the result is correct here too.
Now the problem is when both filters have values it executes both queries together and It doesn't return the correct result. I assume it has something to do with joins. Am I doing something wrong? Please help. And if you need more info or code please let me know. Thank you, you are the best guys!
This is how I execute filters
public function getSearch(BusinessFilter $filters)
{
$businesses = Business::filter($filters)->paginate(30);
return $businesses;
}
This is QueryFilter class. Basically what it does is that it goes through each request parameter and executes its function that was mentioned above.
class QueryFilters{
protected $request;
protected $builder;
public function __construct( Request $request )
{
$this->request = $request;
}
public function apply(Builder $builder)
{
$this->builder = $builder;
foreach( $this->filters() as $name => $value ){
if( !method_exists($this, $name ) ){
continue;
}
if(strlen($value)){
$this->$name($value);
} else {
$this->$name();
}
}
return $this->builder;
}
public function filters()
{
return $this->request->all();
}
}
I have a given table :
tools toolparts parts part_details
----- --------- ----- ------------
id* id* id* id*
name tool_id name part_id
part_id total (int)
----- --------- ----- ------------
the relation between Tools and Parts is ManyToMany. and the relation between parts and part_details is one to many.
with Laravel model, how can I get tool with part that has the biggest part_details.total ??
//tool model
public function parts()
{
return $this->belongsToMany('App\Part', 'tool_part');
}
//part model
public function tools()
{
return $this->belongsToMany('App\Tool', 'tool_part')
}
public function details(){
return $this->hasMany('App\Part_detail');
}
//partDetail model
public function part(){
return $this->belongsTo('App\Part');
}
Controller
public function index()
{
$tools = Tool::with('parts', 'parts.details')->has('parts')->get();
return $tools;
}
what I expected is something like :
Controller
public function index()
{
$tool = Tool::with('SinglePartThatHasHigestTotalInPartDetail');
}
Any Idea ??
You can use Laravel aggregates for querying to get the desired result,
In your code use max() function,
public function index()
{
$tool = Tool::with(['parts.part_details' => function ($query) {
$max = $query->max('total');
$query->where('total',$max);
})->first();
}
I haven't tested this but you can do like this.
Comment if you will get any errors.
I Manage my problem with "hacky" ways. if someone have a better and more elegant solution, please tell me.
//tool model
public function partWithHighestTotalDelivery($trans_date = null){
if (is_null($trans_date)) {
$trans_date = date('Y-m-d');
}
$parts = $this->parts;
$highest_total_delivery = 0;
foreach ($parts as $key => $part) {
$part->detail;
$total_delivery = $part->first_value;
if (isset( $part->detail->total_delivery )) {
$total_delivery += $part->detail->total_delivery;
}
if ($highest_total_delivery < $total_delivery ) {
$highest_total_delivery = $total_delivery;
$part->total_delivery = $highest_total_delivery;
$result = $part;
}
}
if (!isset($result)) {
$result = null;
}
$this->part = $result;
}
In controller i have :
public function index(Request $request){
$tools = Tool::has('parts')
->get();
$tools->each(function($tool){
$tool->partWithHighestTotalDelivery();
});
return $tools;
}
with this, I need to run tool->partWithHighestTotalDelivery() tools.count times. which is take noticeable process if the tools is many.
and also, the code I post and the question I ask has a slightly difference.that's for a simplicity sake's
Use the the hasManyThrough Relationship to get the all part details related to tool and then you can check the one by one record and get the highest total of the tool part.
// Tool Model
public function partsdetails()
{
return $this->hasManyThrough('App\PartDetail', 'App\Part','tool_id','part_id');
}
In Your controller
$data = Tool::all();
$array = [];
if(isset($data) && !empty($data)) {
foreach ($data as $key => $value) {
$array[$value->id] = Tool::find($value->id)->partsdetails()->max('total');
}
}
if(is_array($array) && !empty($array)) {
$maxs = array_keys($array, max($array));
print_r($maxs);// This array return the max total of the tool related parts
}
else{
echo "No Data Available";
}
You can start with the part detail and get the tool(s) from there:
$maxPartDetail = Part_detail::orderByDesc('total')->first();
$tools = $maxPartDetail->part->tools;
After watching many laracasts, one statement is everywhere: keep the controller as light as possible.
Ok, I am trying to familiarize myself with laravel concepts and philosophy, with the Repository and the separation of concerns patterns and I have some questions that bother me, let's assume the following:
Route::resource('/item', 'ItemController');
class Item extends \Eloquent {}
the repo
class EloquentItemRepo implements ItemRepo {
public function all()
{
return Item::all();
}
public function find($id)
{
return Item::where('id', '=', $id);
}
}
and the controller:
class ItemController extends BaseController {
protected $item;
public function __construct(ItemRepo $item)
{
$this->item = $item;
}
public function index()
{
$items = $this->item->all();
return Response::json(compact('items'))
}
}
For now, everything is simple and clean (assume that the repo is loaded by providers etc.) the controller is really simple and does nothing except loading and returning the data (I used json but anything will do).
Please assume that I am using an auth filter that checks that the user
is logged in and exists, or return an error if it doesn't, so I don't
have to do any further check in the controller.
Now, what if I need to do more checks, for instance:
response_* methods are helpers that format a Json response
public function destroy($id)
{
try {
if ($this->item->destroy($id)) {
return Response::json(['success' => true]);
}
return response_failure(
Lang::get('errors.api.orders.delete'),
Config::get('status.error.forbidden')
);
} catch (Exception $e) {
return response_failure(
Lang::get('errors.api.orders.not_found'),
Config::get('status.error.notfound')
);
}
}
In this case I have to test many things:
The desctuction worked? (return true)
The destruction failed? (return false)
There was an error during deletion ? (ex.: the item wasn't found with firstOrFail)
I have methods where many more tests are done, and my impression is that the controller is growing bigger and bigger so I can handle any possible errors.
Is it the right way to manage this ? The controller should be full of checks or the tests should be moved elsewhere ?
In the provider I often use item->firstOrFail() and let the exception bubble up to the controller, is it good ?
If someone could point me to the right direction as all the laracasts or other tutorials always use the simpler case, where not many controls are needed.
Edits: Practical case
Ok so here a practical case of my questioning:
controller
/**
* Update an order.
* #param int $id Order id.
* #return \Illuminate\Http\JsonResponse
*/
public function update($id)
{
try {
$orderItem = $this->order->update($id, Input::all());
if (false === $orderItem) {
return response_failure(
Lang::get('errors.api.orders.update'),
Config::get('status.error.forbidden')
);
}
return response_success();
} catch (Exception $e) {
return response_failure(
Lang::get('errors.api.orders.not_found'),
Config::get('status.error.notfound')
);
}
}
repo
public function update($id, $input)
{
$itemId = $input['itemId'];
$quantity = $input['quantity'] ?: 1;
// cannot update without item id
if (!$itemId) {
return false;
}
$catalogItem = CatalogItem::where('hash', '=', $itemId)->firstOrFail();
$orderItem = OrderItem::fromCatalogItem($catalogItem);
// update quantity
$orderItem->quantity = $quantity;
return Order::findOrFail($id)->items()->save($orderItem);
}
In this case thare are 3 possible problems:
order not found
catalogItem not found
itemId not set in post data
In the way I have organized that, the problem is that the top level error message won't be clear, as it will alway state: "order not found" even if it's the catalog item that couldn't be found.
The only possibility that I see is to catch multiple exceptions codes in the controller and raise a different error message, but won't this overload the controller ?
How do I add custom sort option in Magento. I want to add Best Sellers, Top rated and exclusive in addition to sort by Price. Please help
For Best Sellers
haneged in code/local/Mage/Catalog/Block/Product/List/Toolbar.php method setCollection to
public function setCollection($collection) {
parent::setCollection($collection);
if ($this->getCurrentOrder()) {
if($this->getCurrentOrder() == 'saleability') {
$this->getCollection()->getSelect()
->joinLeft('sales_flat_order_item AS sfoi', 'e.entity_id = sfoi.product_id', 'SUM(sfoi.qty_ordered) AS ordered_qty')
->group('e.entity_id')->order('ordered_qty' . $this->getCurrentDirectionReverse());
} else {
$this->getCollection()
->setOrder($this->getCurrentOrder(), $this->getCurrentDirection());
}
}
return $this;
}
After setCollection I added this method:
public function getCurrentDirectionReverse() {
if ($this->getCurrentDirection() == 'asc') {
return 'desc';
} elseif ($this->getCurrentDirection() == 'desc') {
return 'asc';
} else {
return $this->getCurrentDirection();
}
}
And finally I changed mehod setDefaultOrder to
public function setDefaultOrder($field) {
if (isset($this->_availableOrder[$field])) {
$this->_availableOrder = array(
'name' => $this->__('Name'),
'price' => $this->__('Price'),
'position' => $this->__('Position'),
'saleability' => $this->__('Saleability'),
);
$this->_orderField = $field;
}
return $this;
}
for Top rated
http://www.fontis.com.au/blog/magento/sort-products-rating
try above code.
for date added
Magento - Sort by Date Added
i am not associate with any of the above link for any work or concern it is just for knowledge purpose and to solve your issue.
hope this will sure help you.
Thanks for your answer, Anuj, that was the best working module I could find so far.
Just add an extra bit to your code in order to solve no pagination caused by 'group by'
Copy '/lib/varien/data/collection/Db.php'
To 'local/varien/data/collection/Db.php'.
Change the getSize function to
public function getSize()
{
if (is_null($this->_totalRecords)) {
$sql = $this->getSelectCountSql();
//$this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams); //============================>change behave of fetchOne to fetchAll
//got array of all COUNT(DISTINCT e.entity_id), then sum
$result = $this->getConnection()->fetchAll($sql, $this->_bindParams);
foreach ($result as $row) {//echo'<pre>'; print_r($row);
$this->_totalRecords += reset($row);
}
}
return intval($this->_totalRecords);
}
Hope it could help anyone.
update
The filter section need to be updated as well, otherwise just showing 1 item on all filter.
and the price filter will not be accurate.
What you need to do it to modify core/mage/catalog/model/layer/filter/attribute.php and price.php
attribute.php getCount() on bottom
$countArr = array();
//print_r($connection->fetchall($select));
foreach ($connection->fetchall($select) as $single)
{
if (isset($countArr[$single['value']]))
{
$countArr[$single['value']] += $single['count'];
}
else
{
$countArr[$single['value']] = $single['count'];
}
}
//print_r($countArr);//exit;
return $countArr;
//return $connection->fetchPairs($select);
Price.php getMaxPrice
$maxPrice = 0;
foreach ($connection->fetchall($select) as $value)
{
if (reset($value) > $maxPrice)
{
$maxPrice = reset($value);
}
}
return $maxPrice;
If you are having the same problem and looking for the question, you will know what I meant.
Good luck, spent 8 hours on that best sell function.
Update again,
just found another method to implement
using cron to collect best sale data daily saved in a table that includes product_id and calculated base sale figure.
then simply left join, without applying 'group by'
that means core functions do not need to changed and speed up the whole sorting process.
Finally finished! hehe.
To sort out pagination issue for custom sorting collection rewrite the resource model of it's collection from
app\code\core\Mage\Catalog\Model\Resource\Product\Collection.php
And modify below method from core
protected function _getSelectCountSql($select = null, $resetLeftJoins = true)
{
$this->_renderFilters();
$countSelect = (is_null($select)) ?
$this->_getClearSelect() :
$this->_buildClearSelect($select);
/*Added to reset count filters for Group*/
if(count($countSelect->getPart(Zend_Db_Select::GROUP)) > 0) {
$countSelect->reset(Zend_Db_Select::GROUP);
}
/*Added to reset count filters for Group*/
$countSelect->columns('COUNT(DISTINCT e.entity_id)');
if ($resetLeftJoins) {
$countSelect->resetJoinLeft();
}
return $countSelect;
}
Above will solve count issue for custom sorting collection.