Magento addFieldToFilter allow NULLs - magento

When using the Magento collection method addFieldToFilter is it possible to allow filtering by NULL values? I want to select all the products in a collection that have a custom attribute even if no value is assigned to the attribute.

I see you already found a solution, but there is also this option:
$collection->addFieldToFilter('parent_item_id', array('null' => true));
But if you want to use "NULL" => false, which DOESN'T WORK.
(and I noticed you can use elements such as "in", "nin", "eq", "neq", "gt"), you can do this:
$collection->addFieldToFilter('parent_item_id', array('neq' => 'NULL' ));
Hope this is still helpful...

This works for NOT NULL filters
$collection->addFieldToFilter('parent_item_id', array('notnull' => true));

Filtering a product collection by null/empty attributes has two possible solutions. Magento uses an INNER JOIN to grab the values of the attributes to filter. BUT if the product attribute is not assigned a value the join will fail, as a database table / relationship is missing.
Solution #1: Use addAttributeToFilter() and change the join type from "inner" (the default) to "left":
$collection = Mage::getModel('catalog/product')->getCollection();
$collection->addAttributeToFilter('custom_attribute', array( ... condition options ..), 'left');
Solution #2: Make sure your custom attribute has a default value. Magento is conservative in this regard. Magento will only create the relationship between an attribute and a product if a value is given for the attribute. So in the absence of user-specified value or a default value the attribute will not be accessible for filtering a product even if the attribute appears in the product detail view under the admin panel.

Because the question does not match exactly the title of the question and I found the them multiple times by searching for a condition like: special VALUE OR NULL
If you want to filter a collection matching a VALUE OR NULL, then you can use:
$collection->addFieldToFilter('<attribute>', array(
array('eq' => '<value>'),
array('null' => true)
));

You don't need to use addFieldToFilter.
now the solution:
attributes name is known as code in magento, you just need to use the code below to get all of the products which have a specific attribute as an array
$prodsArray=Mage::getModel('catalog/product')->getCollection()
->addAttributeToFilter('custom_attribute_code')
->getItems();
you can also specify certain conditions for attribute's value in addAttributeToFilter in the second parameter of addAttributeToFilter.
you can find this method in this file (for further study):
app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php

Related

Has laravel 7 model a way to get list of all columns?

Has laravel 7 model a way to get list of all columns ?
I found and tried method
with(new ModelName)->columns
but it returns empty string.
I do not mean $fillable var of a model.
Thanks!
If you just want a reliable way to pull the list of attributes from any given instance no matter the state, and assuming the table structure isn't changing often, the path of least resistance might be to set a defaults attributes array to ensure the attributes are always present.
e.g.
class Fish extends Model
{
protected $attributes = [
'uuid' => null,
'fin_count' => null,
'first_name' => null,
];
}
$fishie = app(\Fish::class);
will then result in an instance of Fish with uuid, fin_count, and first_name set. You can then use $fishie->attributes or $fishie->getAttributes() to load the full set.
Assuming the structure doesn't change a lot, setting the attributes on the model like this will save you a database query every time you want to reference the list. The flip side is that instances change from not having the attributes unless explicitly defined to always being present, which may have implications in the project.
Here's the documentation for default attributes:
https://laravel.com/docs/master/eloquent#default-attribute-values

Laravel validation: unique with multiple columns and soft_delete

I am trying to do a Laravel validation rules as follow:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
The explanation to the rules is:
I have a table "Posts" in my system with the following fields (among others): hotel_id, permalink, deleted_at. If MySQL would allow make an unique index with null values, the sql would be:
ALTER TABLE `posts`
ADD UNIQUE `unique_index`(`hotel_id`, `permalink`, `deleted_at`);
So: I just add a new row IF: the combination of hotel_id, permalink and deleted_atfield (witch must be NULL) are unique.
If there is already a row where the permalink and hotel_id field are the same and 'deleted_at' field is NULL, the validation would return FALSE and the row wouldnt be inserted in the database.
Well. I don't know why, but the query Laravel is building looks like:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `hotel_id` = the-permalink-value AND `NULL` <> deleted_at)
What the heck...
The query I was hoping Laravel build to validation is:
SELECT count(*) AS AGGREGATE FROM `posts`
WHERE `permalink` = 'the-permalink-value' AND `hotel_id` = ? AND `deleted_at` IS NULL
Could someone explain me how this effectively works? Because everywhere I look it looks like this:
$rules = array(
'field_to_validate' =>
'unique:table_name,field,anotherField,aFieldDifferentThanNull,NULL',
);
Does anyone could help me?
Thank you
all.
Finally, I got a proper understanding of the validation (at least, I think so), and I have a solution that, if it is not beautiful, it can helps someone.
My problem, as I said before, was validate if a certain column (permalink) is unique ONLY IF other columns values had some specific values. The problem is the way Laravel validation string rules works. Lets get to it:
First I wrote this:
"permalink" => "required|unique:posts,permalink,hotel_id,deleted_at,NULL|alpha_dash|max:255",
And it was generating bad queries. Now look at this:
'column_to_validate' => 'unique:table_name,column_to_validate,id_to_ignore,other_column,value,other_column_2,value_2,other_column_N,value_N',
So. The unique string has 3 parameters at first:
1) The table name of the validation
2) The name of the column to validate the unique value
3) The ID of the column you want to avoid (in case you are editing a row, not creating a new one).
After this point, all you have to do is put the other columns in sequence like "key,value" to use in your unique rule.
Oh, easy, an? Not so quickly, paw. If you're using a STATIC array, how the heck you will get your "currently" ID to avoid? Because $rules array in Laravel Model is a static array. So, I had to came up with this:
public static function getPermalinkValidationStr() {
$all = Input::all();
# If you are just building the frozenNode page, just a simple validation string to the permalink field:
if(!array_key_exists('hotel', $all)) {
return 'required|alpha_dash|max:255';
}
/* Now the game got real: are you saving a new record or editing a field?
If it is new, use 'NULL', otherwise, use the current id to edit a row.
*/
$hasId = isset($all['id']) ? $all['id'] : 'NULL';
# Also, check if the new record with the same permalink belongs to the same hotel and the 'deleted_at' field is NULL:
$result = 'required|alpha_dash|max:255|unique:posts,permalink,' . $hasId . ',id,hotel_id,' . $all['hotel'] . ',deleted_at,NULL';
return $result;
}
And, in the FrozenNode rules configuration:
'rules' => array(
'hotel_id' => 'required',
'permalink' => Post::getPermalinkValidationStr()
),
Well. I dont know if there is a easiest way of doing this (or a much better approach). If you know something wrong on this solution, please, make a comment, I will be glad to hear a better solution. I already tried Ardent and Observer but I had some problems with FrozenNode Administrator.
Thank you.

Magento: Resource Model 'loadByAttribute' with multiple parameters

I need a way to locate a Magento object my multiple attributes. I can look up an object by a single parameter using the 'loadByAttribute' method, as follows.
$mageObj->loadByAttribute('name', 'Test Category');
However, I have been unable to get this to work for multiple parameters. For example, I would like to be able to do the above query using all of the following search parameters. It might look something like the following.
$mageObj->loadByAttribute(array('entity_id' => 128,
'parent_id' => 1,
'name' => 'Test Category'));
Yes, I know that you don't need all of these fields to find a single category record. However, I am writing a module to export and import a whole website, and I need to test if an object, such as a category, already exists on the target system before I create it. To do this, i have to check to see if an object of the same type, with multiple matching attributes already exists, even if it's ID is different.
This may not answer your question, but it may solve your problem.
Magento does not support loadByAttribute for multiple attributes, but instead you can do this.
$collection = $mageObj->getCollection()
->addAttributeToFilter('entity_id', 128)
->addAttributeToFilter('parent_id', 1)
->addAttributeToFilter('name', 'Test Category');
$item = $collection->getFirstItem();
if ($item->getId()){
//the item exists
}
else {
//the item does not exist
}
addAttributeToFilter works for EAV entities (products, categories, customers).
For flat entities use addFieldToFilter.
There is a special case for sales entities (orders, invoices, creditmemos and shipments) that can use both of them.

How to find associated products from grouped product is they are disabled?

I am trying to get the associated products from a grouped product.I can do that, but not for the products that they are disabled. I tried a solution which mention to set : Use Flat Catalog Product to "NO" but i still can't. Any other ideas? I tried load a collection and use filters like IS_ENABLED OR DISABLED and by loading Models like
$product = Mage::getModel('catalog/product')->load($id);
$associatedProducts = $product->getTypeInstance(true)->getAssociatedProducts($product);
Any other ideas?
So lets look at the getAssociatedProducts() method of Mage_Catalog_Model_Product_Type_Grouped class. Here's the interesting part of it:
if (!Mage::app()->getStore()->isAdmin()) {
$this->setSaleableStatus($product);
}
$collection = $this->getAssociatedProductCollection($product)
->addAttributeToSelect('*')
->addFilterByRequiredOptions()
->setPositionOrder()
->addStoreFilter($this->getStoreFilter($product))
->addAttributeToFilter('status', array('in' => $this->getStatusFilters($product)));
As you can see Magento adds status to collection filter. Method getStatusFilters() returns product statuses to apply on filter. If you would look at the body of this method you would see that it returns basically $product->getData($this->_keyStatusFitlers).
This method needs to return 2 values (2 statuses). But it doesn't. Responsible for that is if statement before the collection set up:
if (!Mage::app()->getStore()->isAdmin()) {
$this->setSaleableStatus($product);
}
This parts will set only ENABLED status on the product status filters.
If you want to get disabled products from grouped product you have rewrite Mage_Catalog_Model_Product_Type_Grouped class and remove the if statement and/or set proper filters.
Let me know if you don't know how to rewrite a Magento class, then I will extend this answer.

How to select specific fields with aliases using joinTable or joinField in Magento

I want to pre-filter* data in the invoice grid visible in Magento's admin panel.
Here is a question that I asked earlier, and this one is related to the solution presented for that, hence it might act as a good explanation.
So, I am modifying the Mage_Adminhtml_Block_Sales_Invoice_Grid::_prepareCollection method so that it first fetches customer referred by the logged in admin. Then it will fetch orders from these customer(s) - ideally only the order id's - Then join this collection to sales/order_invoice_grid, to get invoices to be listed for this admin.
Based on the last answer and using these docs, following are 3 ways I have tried joining this information: (Code Sample 1)
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('*'));
$collection->joinTable('sales/invoice_grid', 'order_id=main_table.entity_id', array('*'));
When I do the above, I see the following error:
A joint field with this alias (0) is already declared.
#0 /var/www/magento/app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php(706): Mage::exception('Mage_Eav', 'A joint field w...')
#1 /var/www/magento/app/code/local/Myproject/Adminhtml/Block/Sales/Invoice/Grid.php(41): Mage_Eav_Model_Entity_Collection_Abstract->joinTable('sales/invoice_g...', 'order_id=main_t...', Array)
#2 /var/www/magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(576): Myproject_Adminhtml_Block_Sales_Invoice_Grid->_prepareCollection()
#3 /var/www/magento/app/code/core/Mage/Adminhtml/Block/Widget/Grid.php(582): Mage_Adminhtml_Block_Widget_Grid->_prepareGrid()
If I remove the second call to joinTable, the above code works, but it is not what I want.
The other method I tried is with this code:
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('entity_id as order_entity_id'));
$collection->joinTable('sales/invoice_grid', 'order_id=main_table.entity_id', array('*'));
Here the error appears in the second line, where I am actually trying to alias the field order.entity_id so that it does not conflict with invoice tables entity_id. However that produces an error like:
Item (Mage_Customer_Model_Customer)
with the same id "1" already exist
I only need order id's so that I can get related invoices, which suggests that I can also use joinField function, which I tried as follows:
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->joinField('order_entity_id', 'sales/order_grid', 'entity_id', 'customer_id=entity_id' , null, 'left');
But it gives me the following error:
Item (Mage_Customer_Model_Customer) with the same id "1" already exist
I am looking for a solution that joins customer->invoices.
By pre-filter I mean that data listed in the grid is filtered even before anything is presented in the grid.
Ok, now my code looks like:
$collection =
Mage::getResourceModel('customer/customer_collection');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('entity_id' => 'order_entity_id'));
And the error that I get is:
SELECT `e`.*, `sales_flat_order_grid`.`order_entity_id` AS `entity_id` FROM `customer_entity` AS `e`
INNER JOIN `sales_flat_order_grid` ON (sales_flat_order_grid.customer_id=e.entity_id) WHERE (e.entity_type_id = '1') ORDER BY `e`.`created_at` desc, `e`.`created_at` desc LIMIT 20
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sales_flat_order_grid.order_entity_id' in 'field list'
Here is the total of my test script. To use put it in a file at the Magento root and type it's URL directly in your browser, it is not handled by Magento's controllers. This is a good way of experimenting as it is not as influenced by other modules, page layouts, etc.
<pre><?php
require 'app/Mage.php';
Mage::app();
$collection = Mage::getResourceModel('customer/customer_collection');
$collection->getSelect()->reset('columns');
$collection->joinTable('sales/order_grid', 'customer_id=entity_id', array('order_entity_id' => 'entity_id'));
$collection->joinTable('sales/invoice_grid', 'order_id=order_entity_id', array('*'));
foreach ($collection as $invoice)
print_r($invoice->debug());
?></pre>
As with your previous question I choose to reset the initial columns because I don't believe in giving the database more work than necessary. However it's not essential, the test still succeeds without it.
If this doesn't work in your installation then we need to consider what that outside influence could be.
The error "A joint field with this alias (0) is already declared." occurs because it uses array keys as aliases. Since you have two joinTable() calls, each with an array, it is trying to use the zero-based index of both and obviously having a conflict.
So instead of
array('entity_id as order_entity_id')
try
array('entity_id' => 'order_entity_id')
to avoid the conflict.
I finally did achieve this by going from invoice->order->customer as 'Anda B' suggested. I am just pasting my solution here as a reference, but will be using this solution from clockworkgeek, since it seems much cleaner. And my solution still needs to be made cleaner by getting the 'id' of eav_attribute (agent_id) from the database at runtime, instead of hard coding it, as pasted here:
class Myproject_Adminhtml_Block_Sales_Invoice_Grid extends Mage_Adminhtml_Block_Sales_Invoice_Grid
{
const AGENT_ID_ATTRIBUTE_ID = 118;
protected function _prepareCollection()
{
$collection = Mage::getResourceModel($this->_getCollectionClass());
$collection->join('order_grid', 'order_id = order_grid.entity_id', array ('order_entity_id' => 'order_grid.entity_id'));
$collection->getSelect()->join( 'customer_entity', 'customer_id = customer_entity.entity_id', array('customer_entity_id' => 'entity_id', 'email'));
$collection->getSelect()->joinLeft( 'customer_entity_int', 'customer_entity_int.entity_id = customer_entity.entity_id AND attribute_id = ' . Myproject_Adminhtml_Block_Sales_Invoice_Grid::AGENT_ID_ATTRIBUTE_ID,
array('attribute_entity_id' => 'customer_entity_int.entity_id', 'attribute_id' , 'value'));
//Apply Desired Data Filters here
$this->setCollection($collection);
return $collection;
It gives you this error "Item (Mage_Customer_Model_Customer) with the same id "1" already exist" because a customer can have multiple orders and so could have two or more entries with the same customer id - you are creating a collection of customers and you must have unique entries in the collection.
You have to start from invoices and join them with the customers.
The simplest way I found out from the magento forum ,
In the
protected function _prepareCollection()
{
$collection = Mage::getResourceModel('customer/customer_collection')
We can use custom queries as
$collection->getSelect()->columns(array('filename' => new Zend_Db_Expr ("(SELECT filename FROM cat WHERE customer_id =e.entity_id)")));
and it works
$this->setCollection($collection);
var_dump($collection);

Resources