Magento dmin Checkboxes Save magento categories to custom database table - magento

I'm currently developing a custom module for Magento and theres one thing I´m not able to get working.
I have a front-end display of employees and an backend so I can add employees.
I save the employees in a regular mysql table(so not EAV). Just to add employees to the database is no problem but now I want to add a different table so that the employees can be part of more than one category. I want to display the magento categories, and that I get working, but next I want to save that value along with the id of my employee in my own table in the database. Thats the problem i'm having.
I've tried using the magento admin grid and have a tab for adding and editing. I´ve tried to add a new tab and adding checkboxes there to check and save but can get it to work
Maybe I'm completely of, if that so youre free to suggest a different approach.

add this to save action
if (isset($data['categories'])) {
$data['categories'] = explode(',', $data['categories']);
if (is_array($data['categories'])) {
$data['categories'] = array_unique($data['categories']);
}
}
and this to collection
$this->getSelect()->join(
array('category_table' => $this->getTable('qbanner/qbanner_category')), 'main_table.qbanner_id = category_table.qbanner_id', array()
)
->where('category_table.category_id = ?', $categoryId);
return $this;
hope this will help you

Related

laravel scout temporary disable toSearchableArray when doing updates

is it possible to disable toSearchableArray when doing updates to a record or is there any way to only update specific fields in a record in my search index?
Eg:
public function toSearchableArray()
{
$item = $this->toArray();
$item['title'] = $this->title;
...
...
...
$item['category'] = $this->category->category_name;
$item['uploaded_at'] = Carbon::now('America/Montreal')->timestamp;
}
The only problem now is each time I update a record it also resets its uploaded_at timestamp and re-loads the relationship which is one more query I dont need since it already has it set when I created the item.
So is there any way I can temporary disable toSearchableArray ? I only need to update a few fields in in the row in my index so there is no need to rerun everything in toSearchableArray
Like bellow only update the title and then update the title in my algolia index without reseting uploaded_at or loading the category relation again
$order = App\Order::find(1);
$order->title = 'a new title'
$order->save();
You can use, unsearchable function available in laravel scout.
$modal->unsearchable();
//etc.....
//Finally save the modal
$modal->save()
This way when you save or update it's won't sync to algolia.
If You again want to sync the model to algolia you may call searchable method as shown below.
$modal->searchable();

Laravel Backpack : Storing Belongs To Many relationships using custom view

I have a flight class and this flight has a custom view field like so:
This represents a belongs to many relationship which stores website_id / flight_id and pricing as pivot data in a pivot table.
The custom view uses JS to send this data back to the controller in this format:
{"1":{"price_adult":"434","price_child":"545"},"2":{"price_adult":"323","price_child":"324"},"3":{"price_adult":"434","price_child":"43"}}
Trying to send this data with the request doesn't create the relations fields, and because I do not have a flight ID at the point of creating this within the controller I can not loop this JSON to make the relations manually.
Can anyone point out what the best course of action is or if there is support for this? I took a look at the docs but they are woefully short and patchy in terms of being much help.
EDIT:
I should have said I can probably make this work using a custom name attribute on the model for the relation, then add a set mutator to loop this data and update the prices relation but I don't want to go down this route if there is support for this I am missing out of the box in backpack.
EDIT2:
Someone asked about the relation:
$this->belongsToMany(Website::class, 'website_pricing')->withPivot('price_adult', 'price_child');
This is working fine its not a problem with the relation working its how can I get backpack to store the data as a relation when the flight has no ID yet, or how can I pass the data I posted above in such a way that the backpack crud controller can handle it?
You may need to create a flight first, if no flight id is being provided. Can you explain the database relational structure more?
Basically thought I should post what I did because no one could provide an answer to this.
So basically you have to copy the store / update functions from the parent, changing a few lines.
$this->crud->hasAccessOrFail('create');
// fallback to global request instance
if (is_null($request)) {
$request = \Request::instance();
}
// replace empty values with NULL, so that it will work with MySQL strict mode on
foreach ($request->input() as $key => $value) {
if (empty($value) && $value !== '0') {
$request->request->set($key, null);
}
}
// insert item in the db
$item = $this->crud->create($request->except(['save_action', '_token', '_method']));
$this->data['entry'] = $this->crud->entry = $item;
// show a success message
\Alert::success(trans('backpack::crud.insert_success'))->flash();
// save the redirect choice for next time
parent::setSaveAction();
return parent::performSaveAction($item->getKey());
Basically any line which references a function in the parent class using $this->method needs to be changed to parent::
This line is what I used to submit the relations JSON string passed to the controller as relations $item->prices()->sync(json_decode($request->input('prices'), true));
This is done after the line containing $item = $this->crud->create as the item id that just got stored will be available at that point.

Laravel: how to filter eloquent data by relationships

I'm building a product catalog in laravel and I would like to filter the products based on their attributes.
I have 2 tables, Products & Attributes. Attributes have a key, value and product_id. For example:
product_id - key - value
12 - brand - BestBrandEver
23 - brand - EvenBetterBrand
Now I would like to be able to filter down my products via a form on the category page which passes query string parameters like "brand=BestBrandEver or brand=EvenBetterBrand" and retrieve only the products from that brand. Eventually I would like to do the same with color, material etc... It's much like Magento's or Woocommerce layered navigation.
I'm stuck for a while now because I don't know how to start building this the right way. Is there someone who could help me with this or is able to point me in the right direction (maybe even with a tutorial or video)?
Thanks in advance!
Assuming a url like this:
/products?filters[brand][]=a&filters[brand][]=b&filters[color][]=a&filters[color][]=b
and so forth...
Use the whereHas method to restrict your products by their attributes:
Product::whereHas('attributes', function ($query) {
foreach (Input::get('filters') as $key => $values) {
$query->orWhere(function($query) use ($key, $values) {
$query->where('key', $key)->whereIn('value', $values);
});
}
});

Magento Collection Catch-22

I don't really understand how Magento collections work, so hopefully this is a simple question...
As the old adage goes, you can't observe an experiment without somehow altering it. This seems to hold true for Magento collections. I have a Featured Products module that I've written that works quite well. We have recently added Customer Reviews to our store. When viewing a category page it shows a random review of products in that category. This also works great. I added the review block to my Featured Products page, which was easy to do, but since those products aren't in a specific category, it just pulls a random, usually unrelated, review. To fix this, I modified my getProductCollection function in my Featured module and added the following to the end, after the collection has been created/saved:
$_product = $this->_productCollection->getFirstItem();
$_catIDs = $_product->getCategoryIds();
if(count($_catIDs) > 0)
{
$_cat = Mage::getModel('catalog/category')->load($_catIDs[0]);
Mage::register('current_category', $_cat);
}
Unfortunately, the mere act of looking at the first item in the collection breaks the pager in the toolbar. No matter which of the paging options I choose, it always shows all items when the above code is in place. If I comment out that section it works fine.
So my question is this: How can I get any information about the products in a collection without somehow changing the collection or breaking the paging?
Adding my code to help explain the problem more:
class VPS_Featured_Block_List extends Amasty_Sorting_Block_Catalog_Product_List//Mage_Catalog_Block_Product_List
{
protected function _getProductCollection()
{
if (is_null($this->_productCollection))
{
$_attributeNames = 'featured';
if($this->getAttributeName() != '')
$_attributeNames = $this->getAttributeName();
$_attrArray = explode(',', $_attributeNames);
$this->_productCollection = Mage::getModel('catalog/product')->getCollection();
$this->_productCollection->addAttributeToSelect('*');
$_filters = array();
foreach($_attrArray as $_attr)
$_filters[] = array('attribute' => $_attr, 'eq' => true);
$this->_productCollection->addFieldToFilter($_filters);
//$this->_productCollection->addFieldToFilter(array(array('attribute' => $_attr, 'eq' => true),));
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($this->_productCollection);
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($this->_productCollection);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($this->_productCollection);
//Get category of first product in collection (used for featured review)
$_catIDs = array();
$_product = $this->_productCollection->getFirstItem();
$_catIDs = $_product->getCategoryIds();
if(count($_catIDs) > 0)
{
$_cat = Mage::getModel('catalog/category')->load($_catIDs[0]);
Mage::register('current_category', $_cat);
}
}
return $this->_productCollection;
}
}
Thanks to #clockworkgeek for this answer he posted in my followup question:
The reason is because when you call getFirstItem() (or just about any other retrieval method) on a collection that collection is loaded. Any subsequent operation ignores the database and uses only the loaded data, filters have no effect because they are SQL only, ditto for pagination and selected columns. The workaround is to use a second collection based on the first.
$secondCollection = clone $firstCollection;
$secondCollection->clear();
$_foo123 = $secondCollection->getFirstItem();
The clear() method unloads the data for that collection, forcing it to access the database again next time
Hard to say what's going on without more context (magento version, which class you're in, etc.) BUT, what I think (60% confidence) it happening is you're getting a reference to the product collection before its filters have been added. Magento collections lazy load, which means database queries aren't run until you explicitly call load or you attempt to access an item. My guess (again, a guess) is that when you access the item above, the collection loads (with no filter). Then, other parts of the system add the filter but they're ignored because the collection is already loaded.
Answering your larger question is impossible without more context.

Adding a custom field to Magento's subscription module

The newsletter subscription module in Magento has only one field (email) by default. After I add an extra field to the form (say country), how can I get the form data to show up in the Magento back-end and be sent as an email to a preset recipient? Thanks.
If you want to add some custom fields for Magento newsletter subscriber (for example subscriber_name), you should do the following:
Add new column for newsletter_subscriber table
Add text input to newsletter template
Create observer for newsletter_subscriber_save_before event
In the observer you can get your custom field's value from request and assign it to subscriber's object:
public function newsletterSubscriberSave(Varien_Event_Observer $observer)
{
$subscriber = $observer->getEvent()->getSubscriber();
$name = Mage::app()->getRequest()->getParam('subscriber_name');
$subscriber->setSubscriberName($name);
return $this;
}
UPDATE:
Here is the detailed article explaining how to add Country field
Also, I have created a free module, it is available on the GitHub
There are a few things that you need to take care of to make this work:
Add a new column for your data to the appropriate database table
Make sure that Magento saves your new field to the database
Present the data in the admin backend
Record the data when you get a new newsletter subscription
Here's how you can do all those things:
Ad. 1)
Using phpMyAdmin, MySQL command line, or whatever is your preferred DB manipulation method, add a new column "country" as, say, varchar(100) to the newsletter_subscriber table.
Ad. 2)
Magento will automatically give you access to the new field through the getCountry() and setCountry() methods on the Mage_Newsletter_Model_Subscriber object. The only thing it won't do is save your field back to the DB after it has been changed with code somewhere in the system. To get it saved you need to modify _prepareSave(Mage_Newsletter_Model_Subscriber $subscriber) function found in Mage_Newsletter_Model_Mysql4_Subscriber (app/code/core/Mage/Newsletter/Model/Mysql4/Subscriber.php). Be sure to make a local copy of the file first and not modify the core file. Here's what you need to add:
protected function _prepareSave(Mage_Newsletter_Model_Subscriber $subscriber)
{
$data = array();
$data['customer_id'] = $subscriber->getCustomerId();
$data['store_id'] = $subscriber->getStoreId()?$subscriber->getStoreId():0;
$data['subscriber_status'] = $subscriber->getStatus();
$data['subscriber_email'] = $subscriber->getEmail();
$data['subscriber_confirm_code'] = $subscriber->getCode();
//ADD A NEW FIELD START
//note that the string index for the $data array
//must match the name of the column created in step 1
$data['country'] = $subscriber->getCountry();
//ADD A NEW FIELD END
(...)
}
Ad. 3)
You will need to modify (a local copy of) the file app/code/core/Mage/Adminhtml/Block/Newsletter/Subscriber/Grid.php. The method you are looking for is called _prepareColumns(). In there you will see a series of calls to $this->addColumn(). You need to add a corresponding call for your "Country" field with the following code:
$this->addColumn('country', array(
'header' => Mage::helper('newsletter')->__('Country'),
//the index must match the name of the column created in step 1
'index' => 'country',
'default' => '----'
));
If you want the field to appear at the end of the grid (as the last column) add it as the last call, otherwise, squeeze it between the existing calls exactly where you want it to end up in the admin.
Ad. 4)
This is a part I did not have to do in my customization of the Magento newsletter, so it will be mostly theoretical. The subscription occurs in the controller located at app/code/core/Mage/Newsletter/controllers/SubscriberController.php. Here's the code of the newAction method with my proposed changes:
public function newAction()
{
if ($this->getRequest()->isPost() && $this->getRequest()->getPost('email')) {
$session = Mage::getSingleton('core/session');
$email = (string) $this->getRequest()->getPost('email');
try {
if (!Zend_Validate::is($email, 'EmailAddress')) {
Mage::throwException($this->__('Please enter a valid email address'));
}
$status = Mage::getModel('newsletter/subscriber')->subscribe($email);
if ($status == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE) {
$session->addSuccess($this->__('Confirmation request has been sent'));
}
else {
$session->addSuccess($this->__('Thank you for your subscription'));
}
//ADD COUNTRY INFO START
//at this point we may safly assume that subscription record was created
//let's retrieve this record and add the additional data to it
$subscriber = Mage::getModel('newsletter/subscriber')->loadByEmail($email);
//assuming that the input's id is "country"
$subscriber->setCountry((string) $this->getRequest()->getPost('country'));
//don't forget to save the subscriber!
$subscriber->save();
//ADD COUNTRY INFO END
}
catch (Mage_Core_Exception $e) {
$session->addException($e, $this->__('There was a problem with the subscription: %s', $e->getMessage()));
}
catch (Exception $e) {
$session->addException($e, $this->__('There was a problem with the subscription'));
}
}
$this->_redirectReferer();
}
Going through the above steps should take care of the most part of your problem. Let me know how that last part worked out, as I did not have a chance to test it.
Once you have your additional field in the Subscriber object you can do whatever you want with it. I did not really get what you mean by
be sent as an email to a preset recipient
If you can explain that I will try to help you out with this part too.
Edit - how to send a mail when someone subscribes
Just add the following code to the controller after the part which adds country to a subscriber object.
$mail = new Zend_Mail();
$mail->setBodyHtml("New subscriber: $email <br /><br />Country: ".$this->getRequest()->getPost('country'));
$mail->setFrom("youremail#email.com")
->addTo("admin#mysite.com")
->setSubject("Your Subject here");
$mail->send();
Adding to the accepted answer, you can also get away with this a little easier if you're adding a date, datetime, or timestamp-type column.
In my case, I wanted to add a "Subscribed at Date" to my grid. To do this, I wrote my upgrade script, column type being TIMESTAMP and the default value being CURRENT_TIMESTAMP. This way, when the row is added, the current date/time is recorded.
Then, all you have to do is add your block customizations. I'd suggest doing it by extending Magento's grid block rather than doing the local codepool override though. This way, you only need to override _prepareColumns();
Old thread but if someone has the same question, there is a free extension, that adds fields for gender, firstname and lastname and makes it available in the backend grid for export via xml/csv: http://www.magentocommerce.com/magento-connect/extended-newsletter-subscription-for-guests.html
Perhaps you can extend the code to fit your needs.
This is a warning for anyone who's installed the Ebizmarts_MailChimp extension.
It's a great extension. But it adds subscriber_firstname and subscriber_lastname to the newsletter_subscriber table.
If you intend to create these fields, you should either "require" the Ebizmarts_MailChimp extension or check the fields don't exist before your extension creates them.
In the opposite, where you've created them and want to install the the Ebizmarts_MailChimp extension after you've created these fields, you will have to comment out the addColumn code for these two fields during installation.

Resources