How to change existing tag information in Magento - magento

I am trying to update the popularity count of Magento's Tag module by interacting with this core function in Mage_Tag_Model_API
public function update($tagId, $data, $store)
{
$data = $this->_prepareDataForUpdate($data);
$storeId = $this->_getStoreId($store);
/** #var $tag Mage_Tag_Model_Tag */
$tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId);
if (!$tag->getId()) {
$this->_fault('tag_not_exists');
}
// store should be set for 'base_popularity' to be saved in Mage_Tag_Model_Resource_Tag::_afterSave()
$tag->setStore($storeId);
if (isset($data['base_popularity'])) {
$tag->setBasePopularity($data['base_popularity']);
}
if (isset($data['name'])) {
$tag->setName(trim($data['name']));
}
if (isset($data['status'])) {
// validate tag status
if (!in_array($data['status'], array(
$tag->getApprovedStatus(), $tag->getPendingStatus(), $tag->getDisabledStatus()))) {
$this->_fault('invalid_data');
}
$tag->setStatus($data['status']);
}
try {
$tag->save();
} catch (Mage_Core_Exception $e) {
$this->_fault('save_error', $e->getMessage());
}
return true;
}
In my controller I have this :
public function clickAction()
{
$tagString = $this->getRequest()->getParam('tag');
$tagByName = Mage::getModel('tag/tag')->loadByName($tagString);
$tagId = $tagByName->getTagId();
$basePopularity = ['base_popularity' => '13']; // hard coding while testing
Mage::getModel('tag/api')->update($tagId, $basePopularity, 1);
}
If I put a log statement in this part of the update function :
try {
// log stuff
$tag->save();
}
I can see it makes it to that try but there is no change in the data. What did I screw up? Any other ideas on how I can update the popularity of a tag through a controller? Using this same method and adding 'name' => 'blah' to that $data array parameter works fine..
I also found in Mage_Tag_Model_Indexer_Summary.php this method defined in the PHPdoc * #method Mage_Tag_Model_Indexer_Summary setPopularity(int $value) Maybe that is what I need... can someone provide an example showing how I could use that magic setter?

Try adding Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID); at the start of your clickAction function. base_popularity can only be updated from admin store.

Related

Laravel - How to update Input Array without deleting Sales Detail

In my Laravel-8 project, I have this controller for Input Field Array Update.
Controller:
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
SaleDetail::where('sale_id', $sale->id)->delete();
foreach ($data['invoiceItems'] as $item) {
$details = [
'sale_id' => $sale->id,
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$saleDetail = new SaleDetail($details );
$saleDetail->save();
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
In the form, the user can add more Sales Detail or remove.
Some of the SaleDetail fields are being used somewhere else.
Is there a way to update the input field array without deleting the SaleDetail as shown in what I did here:
SaleDetail::where('sale_id', $sale->id)->delete();
Thanks
I've tried to restructure your code so that's easier to edit. I've left some comments. I can really recommend refactoring.guru. There you will find many ways to improve your code so that it is more extensible, maintainable and testable. If you have any questions, please feel free to ask.
class Sale extends Model
{
// Use a relationship instead of building your own query
public function details() {
return $this->hasMany(SaleDetail::class);
}
}
class SaleDetail extends Model
{
// Use a computed property instead of manually calculating total price
// You can access it with $saleDetail->totalPrice
public function getTotalPriceAttribute() {
return $this->price * $this->quantity;
}
}
class UpdateSaleRequest extends Request
{
public function authorize() {
return true;
}
protected function prepareForValidation() {
$this->merge([
// Create a Carbon instance by string
'date' => Carbon::make($this->date)
]);
}
public function rules() {
// Your validation rules
// Please also validate your invoice items!
// See https://laravel.com/docs/8.x/validation#validating-arrays
}
}
// We let Laravel solve the sale by dependency injection
// You have to rename the variable name in ihr web.php
public function update(UpdateSaleRequest $request, Sale $sale)
{
// At this point, all inputs are validated!
// See https://laravel.com/docs/8.x/validation#creating-form-requests
$sale->update($request->validated());
// Please ensure, that all properties have the same name
// In your current implementation you have price = cost, be consistent!
foreach($request->input('invoiceItems') as $invoiceItem) {
// How we can consider that a detail is already created?
// I assume that each item_id will only occur once, otherwise you'll
// place the id of each detail in your update form (e.g. in a hidden input)
$candidate = $sale->details()
->where('item_id', $properties['item_id'])
->first();
if($candidate) {
$candidate->update($properties);
} else {
$sale->details()->create($properties);
}
}
// A JWT-Exception should not be necessary, since your authentication
// will be handled by a middleware.
return response()->json($sale);
}
I have not tested the code, few adjustments may be needed.
Laravel has a method called updateOrCreate as follow
/**
* Create or update a record matching the attributes, and fill it with values.
*
* #param array $attributes
* #param array $values
* #return \Illuminate\Database\Eloquent\Model|static
*/
public function updateOrCreate(array $attributes, array $values = [])
{
return tap($this->firstOrNew($attributes), function ($instance) use ($values) {
$instance->fill($values)->save();
});
}
That means you could do some thing like
public function update(UpdateSaleRequest $request, $id)
{
try {
$sale = Sale::find($id);
$data = $request->all();
$update['date'] = date('Y-m-d', strtotime($data['date']));
$update['company_id'] = $data['company_id'];
$update['name'] = $data['name'];
$update['remarks'] = $data['remarks'];
$sale->update($update);
foreach ($data['invoiceItems'] as $item) {
$details = [
'item_id' => $item['item_id'],
'employee_id' => $item['employee_id'],
'quantity' => $item['qty'],
'price' => $item['cost'],
'total_price' => $item['cost'] * $item['qty'],
'sale_type_id'=>$item['sale_type_id']
];
$sale->saleDetail()->updateOrCreate([
'sale_id' => $sale->id
], $details);
}
} catch (JWTException $e) {
throw new HttpException(500);
}
return response()->json($sale);
}
I would encourage you to refactor and clean up your code.You can also read more about it here https://laravel.com/docs/8.x/eloquent#upserts

Magento - steps checkout and additional module

Thanks for this forum. I often read it and this is my first question.
The question concerns Magento (1.9.1.0). I wanted to add an additional module that allows customer to write a comment on the order. I've been able to find a module that suits my need on the web. It works perfectly, but there is a big problem: it joins in the final step of the checkout (in particular, after the payment phase). And so, if the customer pays by bank transfer, marking or similar payments, the problem doesn't arise: Magento passes to the next step (order comment) and then order completion.
The problem arises in case of Paypal payment, as Magento redirects customer to Paypal.com and, after making the payment, returns to the store by the end of the order (without going to the next step of the comment). The comment form consisted of 19 files that, once installed in Magento, don't modify any existing files.
I would like to include this form after shipping_method step (and not after payment step) and, therefore, I think I have to modify the file (of the module) that is called Abstract.php (app/code/community/Brainvire/Ordercomment/Controller/Onepage/Abstract.php).
I would be very grateful if you could help me edit that file.
<?php
require_once 'Mage/Checkout/controllers/OnepageController.php';
class Brainvire_Ordercomment_Controller_Onepage_Abstract extends
Mage_Checkout_OnepageController {
/*
* Saving the Payment at Checkout
*/
public function savePaymentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('payment', array());
try {
$result = $this->getOnepage()->savePayment($data);
}
catch (Mage_Payment_Exception $e) {
if ($e->getFields()) {
$result['fields'] = $e->getFields();
}
$result['error'] = $e->getMessage();
}
catch (Exception $e) {
$result['error'] = $e->getMessage();
}
$redirectUrl = $this->getOnePage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
if (empty($result['error']) && !$redirectUrl) {
$this->loadLayout('checkout_onepage_ordercomment');
$result['goto_section'] = 'ordercomment';
}
if ($redirectUrl) {
$result['redirect'] = $redirectUrl;
}
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
/*
* Saving the order comment
*/
public function saveOrdercommentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$_brainvire_Ordercomment = $this->getRequest()->getPost('ordercomment');
Mage::getSingleton('core/session')->setBrainvireOrdercomment($_brainvire_Ordercomment);
$result = array();
$redirectUrl = $this->getOnePage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
if (!$redirectUrl) {
$this->loadLayout('checkout_onepage_review');
$result['goto_section'] = 'review';
$result['update_section'] = array(
'name' => 'review',
'html' => $this->_getReviewHtml()
);
}
if ($redirectUrl) {
$result['redirect'] = $redirectUrl;
}
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
}
How do I edit this php code to insert Ordercomment after shipping_method step and before payment step?
Thanks
GS
I was able to move the order-comment section by editing two files:
First: Brainvire\Ordercomment\Block\Onepage.php
class Brainvire_Ordercomment_Block_Onepage extends Mage_Checkout_Block_Onepage
{
/**
* getting the steps of Checkout page
*/
public function getSteps()
{
...
$stepCodes = array('billing', 'shipping', 'shipping_method', 'ordercomment', 'payment', 'review'); // moved 'ordercomment' before 'payment'
...
}
}
And then the file you mentioned: Brainvire\Ordercomment\Controller\Onepage\Abstract.php
Remove the savePaymentAction() method and replace with this:
class Brainvire_Ordercomment_Controller_Onepage_Abstract extends Mage_Checkout_OnepageController {
/**
* Shipping method save action
*/
public function saveShippingMethodAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->getRequest()->isPost()) {
$data = $this->getRequest()->getPost('shipping_method', '');
$result = $this->getOnepage()->saveShippingMethod($data);
/*
$result will have erro data if shipping method is empty
*/
if(!$result) {
Mage::dispatchEvent('checkout_controller_onepage_save_shipping_method',
array('request'=>$this->getRequest(),
'quote'=>$this->getOnepage()->getQuote()));
$this->getOnepage()->getQuote()->collectTotals();
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
if (empty($result['error']) && !$redirectUrl) {
$this->loadLayout('checkout_onepage_ordercomment');
$result['goto_section'] = 'ordercomment';
}
}
$this->getOnepage()->getQuote()->collectTotals()->save();
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
}
}
/*
* Saving the order comment
*/
public function saveOrdercommentAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
$_brainvire_Ordercomment = $this->getRequest()->getPost('ordercomment');
Mage::getSingleton('core/session')->setBrainvireOrdercomment($_brainvire_Ordercomment);
$result = array();
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
}
Once you get this working, you should move these customizations into an override module, or at least copy the modified files to app/code/local, so the original code stays intact.

Mage_Core_Exception with message 'Cannot retrieve entity config: sales/Array'

The following code runs fine under Magento 1.6 but raises a Mage_Core_Exception (message: 'Cannot retrieve entity config: sales/Array') when run under 1.5.0.1. What do I need to do to get this code running under Magento 1.5.0.1?
$results = Mage::getResourceModel('sales/order_collection');
$results->join(
array('status_key_table' => 'order_status'),
'main_table.status = status_key_table.status',
array('status_key_table.label')
);
Thank you,
Ben
If you compare the join() methods between 1.5.0.1 and 1.6.2.0:
1.5.0.1: Mage_Core_Model_Mysql4_Collection_Abstract::join()
public function join($table, $cond, $cols='*')
{
if (!isset($this->_joinedTables[$table])) {
$this->getSelect()->join(array($table=>$this->getTable($table)), $cond, $cols);
$this->_joinedTables[$table] = true;
}
return $this;
}
1.6.2.0: Mage_Core_Model_Resource_Db_Collection_Abstract::join()
public function join($table, $cond, $cols = '*')
{
if (is_array($table)) {
foreach ($table as $k => $v) {
$alias = $k;
$table = $v;
break;
...
You can see that 1.5.0.1 doesn't support aliases. Instead, it calls $this->getTable() on the first parameter you pass in -- which should be a string. So, in your case, you'll need to pass in 'sales/order_status' as the first parameter.

How to return to edit form?

I have this code in my controller (admin):
function save(){
$model = $this->getModel('mymodel');
if ($model->store($post)) {
$msg = JText::_( 'Yes!' );
} else {
$msg = JText::_( 'Error :(' );
}
$link = 'index.php?option=com_mycomponent&view=myview';
$this->setRedirect($link, $msg);
}
In model I have:
function store(){
$row =& $this->getTable();
$data = JRequest::get('post');
if(strlen($data['fl'])!=0){
return false;
}
[...]
And this is working - generate error message, but it return to items list view. I want to stay in edit view with entered data. How to do it?
In your controller you can:
if ($model->store($post)) {
$msg = JText::_( 'Yes!' );
} else {
// stores the data in your session
$app->setUserState('com_mycomponent.edit.mymodel.data', $validData);
// Redirect to the edit view
$msg = JText::_( 'Error :(' );
$this->setError('Save failed', $model->getError()));
$this->setMessage($this->getError(), 'error');
$this->setRedirect(JRoute::_('index.php?option=com_mycomponent&view=myview&id=XX'), false));
}
then, you will need to load the data from session with something like:
JFactory::getApplication()->getUserState('com_mycomponent.edit.mymodel.data', array());
normally this is loaded in the method "loadFormData" in your model. Where to load that data will depend on how are you implementing your component. If you are using the Joomla's form api then you can add the following method to your model.
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = JFactory::getApplication()->getUserState('com_mycomponent.edit.mymodel.data', array());
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
EDIT:
BUT please note, that Joomla's API already can do all this for you if you controller inherits from "JControllerForm", you don't need to rewrite the save method. The best way to create your component is copying what is in Joomla's core components, com_content for example
It is not recommended to rewrite save or any method.
If you really want to override something and want to update something before or after save, you should use JTable file.
For Example:
/**
* Example table
*/
class HelloworldTableExample extends JTable
{
/**
* Method to store a node in the database table.
*
* #param boolean $updateNulls True to update fields even if they are null.
*
* #return boolean True on success.
*/
public function store($updateNulls = false)
{
// This change is before save
$this->name = str_replace(' ', '_', $this->name);
if (!parent::store($updateNulls))
{
return false;
}
// This function will be called after saving table
AnotherClass::functionIsCallingAfterSaving();
}
}
You can extends any method using JTable class and that's the recommended way to doing it.

Form validation with custom callback function

I created a "callback" function to check if the username exists in the DB.
I have multiple rules for the "username" field, but the only thing that work is my callback function. It refuses to check against the other rules. I tried leaving the field empty, and the "required" rule never kicked in.
Controller:
account.php
function register() {
$this->load->library('validation');
$fields['username'] = "trim|required|callback_username_check";
etc ...
etc ...
$this->validation->set_rules($fields);
if ($this->validation->run()) {
$records = array();
$records['username'] = $this->validation->username;
etc ...
etc ...
$data = $this->account_model->registerNewAccount($records);
}
$this->load->view('register_view');
}
function username_check($username) {
$m = new Mongo();
$collection = $m->selectDB( DBNAME )->selectCollection( TABLE );
$data = $collection->count(array("username" => $username) );
if($data == 1) {
$this->validation->set_message('username_check', '%s is already taken!');
return false;
} else {
return true;
}
}
Try using the new form_validation class here:
http://ellislab.com/codeigniter/user_guide/libraries/form_validation.html
I believe there was a bug about it.

Resources