Magento Custom Sort Option - magento

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.

Related

How to apply the promotion code only once for a product in laravel

I need to apply the promotion code only once for a product, when I apply the same code for the same product it should not get applied.
Find below the code :
public function apply_promo(Request $request)
{
$name = $request->input();
$code=$name['code'];
$code_flags=0;
$p_id='';
$discount_value=0;
$discount_type='';
$Object = new Promotion();
$response = $Object->GetPromotion();
//print_r($response);exit;
foreach($response['promotions']['promotion'] as $value)
{
if($value['code']==$code)
{
$code_flags=1;
$p_id=$value['requires'];
$discount_value=$value['value'];
$discount_type=$value['type'];
}
}
if($code_flags==1)
{
$p_id_array=explode(',',$p_id);
$flags=0;
$data=json_encode(Cart::content());
$cartdata=json_decode($data);
//echo "<pre>";print_r($cartdata);exit;
foreach($cartdata as $key => $value)
{
if($value->options->package != 'domain')
{
if(in_array($value->options->gid,$p_id_array))
{
$flags=0;
$price=$value->price;
if($discount_type=='Percentage')
{
$discount_p=(($price*$discount_value)/100);
$price=$price-$discount_p;
$value->options->discount_code = $code;
$value->options->discount_price = $discount_p;
$value->options->discount_percentage = $discount_value;
Cart::update($value->rowId, ['price' => $price]);
}
}
}
}
if($flags==0)
{
session()->put('promo_code', $code);
\Session::flash('message');
return redirect('cart?success');
}
}
else{
session()->put('promo_code', '');
\Session::flash('message_error');
return redirect('cart?error');
}
}
I have tried using the above code, but here the code getting applied as many times as i submit the code. Kindly suggest me a solution to solve this.
you can make table the have used promotions and their product , say we name it product_promotions have ( promo_id , product_id ) .
then in product model create relation called promocodes (many to many)
public function promocodes(){
return $this->belongsToMany(Promo::class)->withPivot(['promo_id']);
}
this relation will return all promos used by this products so we now able to check if promo used before by it using this code
$product->promocodes()->where('promo_id' , $your_promo_code_id)->exists();

laravel many to many with chaining where clause

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;

How to insert array data in laravel 5.4

I have a product table and device table.
I have joined these two tables where I have selected device.id & product.name, now I want to insert this data into my existing device table using device.id.
I want to insert multiple dropdown values in my database. Can anyone tell me how can I solve this problem? Below is my Controller Code-
foreach ($request->all() as $value)
{
$deviceById= Device::find($request->id);
if($request->destination_type == 'dealer')
{
$deviceById->destination_location=$value->dealer_id;
}else
{
$deviceById->destination_location=$value->office_id;
}
$deviceById->save();
}
flash('Device Dispatch added successfully!')->success()->important();
return redirect()->back();
You can make array of all dropdowns value and convert into a json string and store it in database
You can do something like this. Change according to your requirement. It just a logic demo.
$deviceById= Device::find($request->id);
$destination = array();
foreach ($request->all() as $value)
{
if($request->destination_type == 'dealer')
{
$destination[] = $value->dealer_id;
}else
{
$destination[] = $value->office_id;
}
}
$jsonString = json_encode($destination);
$deviceById->destination_location = $jsonString
$deviceById->save();
flash('Device Dispatch added successfully!')->success()->important();
return redirect()->back();
I solve it this way.I had used Elequent ORM
if($request->destination_type == 'Dealer')
{
DB::table('devices')
->whereIn('id',$request->device_list)
->update(['dealer_id'=>$request->destination_location,'device_status'=>"Dispatch"]);
flash('Device Dispatch dealer added successfully!')->success()->important();
}else
{
DB::table('devices')
->whereIn('id',$request->device_list)
->update(['office_id'=>$request->destination_location,'device_status'=>"Dispatch"]);
flash('Device Dispatch office added successfully!')->success()->important();
}

Laravel 5.4 Collection Map Return Values

I'm having trouble creating a function on collection map with return values.
public function getCollectionFakeId($collection, $fieldNames){
$optimus = $this->optimus;
$result = $collection->map(function($item, $key) use ($optimus, $fieldNames) {
return [
$fieldNames[0] =>$optimus->encode($item->id),
$fieldNames[1] => $item->lastname
];
}) ;
dd($result);
return json_decode(json_encode($result), FALSE);
}
As you can see the return fieldNames[0] is being hardcoded. I don't know how many fieldNames it will received. I need to return those fieldnames with obfuscated Id. So basically The only changed is the Id. Here is the screenshot.
As you can see the fieldNames are just 2 but what if it becomes 5 or 6. I don't really know how many fieldNames they are going to pass in the parameter. How can I return it. Thanks.
In case someone will encounter this problem. Here is my solution...
public function getCollectionFakeId($collection, $fieldNames){
$optimus = $this->optimus;
$result = $collection->map(function($item, $key) use ($optimus, $fieldNames) {
$mapFieldNames = array_map(function($v) use ($optimus, $item) {
if( $v == 'id'){
return $optimus->encode($item->id);
}
else{
return $v;
}
}, $fieldNames);
return $mapFieldNames;
}) ;
dd($result);
return json_decode(json_encode($result), FALSE);
}
The result is the same. AWESOME!

Can we prevent an item being shipped if the item has not been invoiced. Magento

Ideally we would like to prevent any items on an order from being shipped if they do not have an invoice created.
At the moment Magento (1.7.0.2) allows the creation of a shipment even if there has been no invoices created for the order. I realise this is by design for site owners who wish to ship first and invoice afterwards but our workflow requires payment first on creation of the invoice before allowing a shipment.
Could anyone please offer any suggestions code wise on what to change? I think the correct file would be /app/code/local/Mage/Sales/Model/Order/Shipment.php and the code we require to change would be below but im not sure how to check each item for an invoice individually.
Edit - Would the following code work well?
protected function _beforeSave()
{
if ((!$this->getId() || null !== $this->_items) && !count($this->getAllItems())) {
Mage::throwException(
Mage::helper('sales')->__('Cannot create an empty shipment.')
);
}
$order = $this->getOrder();
if ($order->canInvoice()) {
Mage::throwException(
Mage::helper('sales')->__('Cannot ship items without an Invoice.')
);
}
You can get some hints about the code to use by looking at this piece of code from: \app\code\core\Mage\Sales\Model\Order.php in the public function canInvoice():
public function canInvoice()
{
if ($this->canUnhold() || $this->isPaymentReview()) {
return false;
}
$state = $this->getState();
if ($this->isCanceled() || $state === self::STATE_COMPLETE || $state === self::STATE_CLOSED) {
return false;
}
if ($this->getActionFlag(self::ACTION_FLAG_INVOICE) === false) {
return false;
}
foreach ($this->getAllItems() as $item) {
if ($item->getQtyToInvoice()>0 && !$item->getLockedDoInvoice()) {
return true;
}
}
return false;
}

Resources