Can't add additional where queries in Ardent Model - laravel

I'm having a problem using LaravelBook/Ardent. My logic is exclude the soft deleted rows in unique validation using the code:
public static $rules = array(
'name' => 'required|unique:paper_colors,name,deleted_at,NULL',
'description' => 'required|between:2,255',
'code' => 'required'
);
But when I run the updateUniques I'm still getting The name has already been taken. and this sql:
select count(*) as aggregate from `paper_colors` where `name` = '4/0' and `id` <> '2'
I'm expecting the sql will be:
select count(*) as aggregate from `paper_colors` where `name` = '4/0' and `id` <> '2' and `deleted_at` is null
Can someone help me to solve this. I'm stuck almost last night on this. Still can't figure it out how to deal with this.

I found out that Ardent is dropping the Laravel Validation feature: Adding Additional Where Clauses
So my solution to overcome this bug is to add additional code under LaravelBook\Ardent\Ardent#buildUniqueExclusionRules at line: 799, but I didn't do that since I'm depending for their any updates in the future. So I just create a class that extend LaravelBook\Ardent\Ardent and copy buildUniqueExclusionRules and modify it.
if (count($params)>2)
{
$c = count($uniqueRules);
for ($i=1; $i < count($params); $i++, $c++) {
$uniqueRules = array_add($uniqueRules, $c, $params[$i]);
}
}

Related

CodeIgniter 3.1.7 unbuffered_row() returning NULL but row() works

I am attempting to output a CSV from a query result. dbutil->csv_from_result() should work, but it only returns the column headers. No data is included.
I tracked it down to system/database/DB_utility.php which uses unbuffered_row().
Any calls to unbuffered_row() return NULL. If I change it to row(), I get a result. It makes no difference if I use unbuffered_row('array') or unbuffered_row('object')
Why does row() work but unbuffered_row() does not?
Is this a bug in CI or am I missing something?
Edit: row('array') doesn't seem to work either.
Edit: It seems that calling $query->result() spoils dbutil->csv_from_result($query). You apparently cannot iterate through query results AND then save the results in a CSV file. This was possible in CI 2.
Is there any way to show query results AND save the CSV without running the query twice?
I have faced similar problem and found this:
I have this query:
$query = $this->db->query("SELECT * FROM tablename");
print_r($query);
Then I get this and unbuffered_row works fine:
CI_DB_oci8_result Object
(
[stmt_id] => Resource id #106
[curs_id] =>
[limit_used] =>
[commit_mode] => 32
[conn_id] => Resource id #91
[result_id] => 1
[result_array] => Array
(
)
[result_object] => Array
(
)
[custom_result_object] => Array
(
)
[current_row] => 0
[num_rows] =>
[row_data] =>
)
BUT if I call $query->num_rows() before I get a different CI_DB_oci8_result object and unbuffered_row didn't work returning null
$query = $this->db->query("SELECT * FROM tablename");
echo $query->num_rows();
print_r($query);
Hope this help somebody.

Magento, filter collection by date period and include null values

I'm having a difficult time settling up a dates filter on a collection with custom tables. Have searched here and here and some other places but still can not get what I need. The problem is that I can't add the NULL values to the result set.
So far my current code after several trial and error tests:
$myBannersCollection = Mage::getModel('banners/bannersadmin')->getCollection()
->addfieldtofilter('banner_prod_id',$currentProdID)
->addfieldtofilter('banner_start_date',
array(
array('from' => Mage::getModel('core/date')->gmtDate()),
'banner_start_date' => null))
->addfieldtofilter('banner_end_date',
array(
array('gteq' => Mage::getModel('core/date')->gmtDate()),
'null' => true)
);
var_dump((string) $myBannersCollection->getselect());
This code ouputs the following sql snippet:
SELECT `main_table`.*
FROM `dts_banners_admin` AS `main_table`
WHERE (banner_prod_id = '16')
AND (((banner_start_date >= '2012-11-28 14:39:13') OR (banner_start_date='')))
AND (banner_end_date IS NULL)
Have tried several different options to add the NULL condition but no way I can get something like:
SELECT `main_table`.*
FROM `dts_banners_admin` AS `main_table`
WHERE (banner_prod_id = '16')
AND (((banner_start_date>='2012-11-28 14:39:13') OR (banner_start_date IS NULL)))
AND ((banner_end_date >= '2012-11-28 14:39:13') OR (banner_end_date IS NULL))
PS: does Magento has a BETWEEN operator on the addfieldtofilter?
Got it! Thanks to this SO answer. Needed to add another array just for the IS NULL clause. Now the code is:
$myBannersCollection = Mage::getModel('banners/bannersadmin')->getCollection()
->addfieldtofilter('banner_prod_id',$currentProdID)
->addfieldtofilter('banner_start_date',
array(
array('to' => Mage::getModel('core/date')->gmtDate()),
array('banner_start_date', 'null'=>'')))
->addfieldtofilter('banner_end_date',
array(
array('gteq' => Mage::getModel('core/date')->gmtDate()),
array('banner_end_date', 'null'=>''))
);
And outputs this:
SELECT `main_table`.*
FROM `dts_banners_admin` AS `main_table`
WHERE (banner_prod_id = '16')
AND (((banner_start_date>='2012-11-28 15:12:03') OR (banner_start_date IS NULL)))
AND (((banner_end_date >= '2012-11-28 15:12:03') OR (banner_end_date IS NULL)))
EDIT
Modified the banner_start_date as I was using from instead of to and so the period was incorrectly settled up and no data was returned.

Joining AND conditions with OR condition on Magento

I am able to run AND and OR boolean conditions on Magento using addFieldToFilter and addAttributeToFilter.
But, I am curious about implementing more complex boolean conditions like below:
WHERE (`sku > 5` AND `price` > '10') OR (`name` = 'books')
for AND condition, I have
$collections = Mage::getModel('catalog/product')->getCollection()->addAttributeToSelect('*')
->addFieldToFilter( 'sku', array('gt' => 5 )
->addFieldToFilter('price', array('gt' => 10);
Which translates to the first parenthesis of the WHERE condition:
(`sku` > 5 AND `price` > 10)
I do not know how to proceed from here to express the OR condition
`name` = 'books'
Any help will be appreciated. Thanks
See my earlier post:
complex boolean conditions on Magento
You can create OR by passing an array:
Mage::getModel('catalog/product')
->getCollection()
->addFieldToFilter('price', array('gt' => 10))
->addAttributeToFilter(
array(
array('attribute'=>'firstname', 'like'=>'test%'),
array('attribute'=>'lastname', 'like'=>'test%'),
)
)
Each additional call to addAttributeToFilter() will ANDed, but firstname / lastname will be ORed.
If you need more complex select statements you can always get the select object from the collection and perform adjustments to the query. Look at Varien_Db_Select for hints :)
$collection->getSelect()->join(..)->orWhere(..); // etc
You can even debug the query to see if it's looking ok:
echo $collection->getSelect()->__toString();

CodeIgniter is discarding JOIN clause in SQL statement (Active Record)

I have the following function in one of my CI models:
function update_uu_feature($feature, $lang, $data)
{
$feature = str_replace('_', '-', $feature);
$this->db->where('navigation', $feature);
$this->db->where('language', $lang);
$this->db->join('all_video_names', 'all_video_names.video_id = all_uu_features.video_id', 'inner');
return $this->db->update('all_uu_features', $data);
}
But the “join” line gets discarded. In the profiler, it outputs:
UPDATE `all_uu_features` SET `page_title` = 'Laser', `title` = 'Test', `keywords` = 'Test', `description` = 'Test', `feature_text` = '<p>Test text</p>', `learn_more_about` = 'Test Text 2', `overview` = 'test', `copy` = 'test' WHERE `navigation` = 'laser-interface' AND `language` = 'en'
Can someone help me with the syntax? Thanks. Most of the data is in the all_uu_features table, except for the video name, which is in the all_video_names table. The two are joined on the video_id column in each table. In other words, all of the columns in the above statement are in all_uu_features except for video name. Hope that's clear.
The SQL statement above shows the data is being returned in the post and being processed by the function, it's just not accepting the join for some reason.
It looks to me very much like CI's Active Record class doesn't support using joins in its update() operation. Certainly if you have a look at the code (in DB_active_rec.php), the update() method doesn't even look at any joins that have been set up
From update():
$sql = $this->_update($this->_protect_identifiers($table, TRUE, NULL, FALSE), $this->ar_set, $this->ar_where, $this->ar_orderby, $this->ar_limit);

Where statements are not being appended

I am trying to build a query dynamically. It's initial state is fine. This is the initial where clause that should be present on every query
$qb->add('where', $qb->expr()->andx(
$qb->expr()->eq('s.competitor', $competitor),
$qb->expr()->eq('s.ignored', $ignored),
$qb->expr()->eq('s.id', $params['s_id']),
$qb->expr()->eq('s.id', 'k.targetSite')
), true);
But the app I am building allows users to filter. When that happens, I want to add additional where clauses into my query builder. When this line is executed later in the code, it overwrites the above where statement.
$qb->add('where', $qb->expr()->like($col, $val), true );
From what I have read, the 3rd parameter $append should keep the previous statements, but that's not happening. In Doctrine 1.2, I could just do something like this:
foreach($filter as $col => $val) {
$dql->addWhere($col = ?, array($val));
}
How do I dynamically add where clauses to my QueryBuilder?
Update
Here is a full statement
$where = array('col' => 'k.text', 'val' => 'some word%');
$qb = $this->entityManager->createQueryBuilder()
->select('s, sc')
->from('Dashboard\Entity\Section', 'sc')
->innerJoin('sc.keyword', 'k')
->innerJoin('sc.site', 's')
->leftJoin('k.keywordCategory', 'kc')
->leftJoin('k.keywordSubCategory', 'ksc');
$qb->add('where', $qb->expr()->andx(
$qb->expr()->eq('s.competitor', $competitor),
$qb->expr()->eq('s.ignored', $ignored),
$qb->expr()->eq('s.id', $params['s_id']),
$qb->expr()->eq('s.id', 'k.targetSite')
), true);
if ($where) {
$qb->add('where', $qb->expr()->andx(
$qb->expr()->like($where['col'], $where['val'])
), true);
}
$qb->addGroupBy('k.id');
$qb->addGroupBy('s.id');
$qb->setFirstResult( $params['start'] )
->setMaxResults( $params['limit'] );
$q = $qb->getQuery();
echo $q->getSql();
And the output is
SELECT s0_.id AS id0, k1_.id AS id1, k1_.name AS name2, k2_.id AS id3, k2_.name AS name4, k3_.id AS id5, k3_.text AS text6, k3_.search_vol AS search_vol7, s4_.id AS id8, s4_.sub_domain AS sub_domain9, MIN(s0_.rank) AS sclr10, MAX(s0_.created) AS sclr11
FROM section s0_
INNER JOIN keyword k3_ ON s0_.k_id = k3_.id
INNER JOIN site s4_ ON s0_.s_id = s4_.id
LEFT JOIN keyword_category k1_ ON k3_.k_cat_id = k1_.id
LEFT JOIN keyword_sub_category k2_ ON k3_.k_subcat_id = k2_.id
WHERE k3_.text LIKE 'some word%'
GROUP BY k3_.id, s4_.id LIMIT 25 OFFSET 0
If I don't add in that if ($where) clause, then the first andx where statements are still in place. But when I try to dynamically add them, only the final WHERE statement is added, all others are cleared. I should also add, that I tried it like this as well.
if ($where) {
$qb->add('where', $qb->expr()->like($where['col'], $where['val']), true);
}
I can successfully use
$qb->andWhere( $qb->expr()->like($where['col'], $where['val']) );
But the API docs for Query Builder state the way I am trying to use it should be valid too. Wanted to make sure I was doing it right, or if it was a bug.
You're able to use the ->andWhere() normally (and it will also fixes your issue).
Doctrine 2 QueryBuilder is a rather innovative concept (because it mixes both programmatic and fluent styles), and there are likely bugs associated to it.
One point that you should notice in your code: Instead of play with ->add('where', ...), you should think programmatic. Add more items to andX() object and at the end associate to the ->add('where', ...) (or even better: ->where(...))
Looking at the code, it seems like what you're trying to do won't work, though whether that's documented anywhere or is just a bug is open to question, I think.
In add(), the DQL part you pass in seems only ever to be appended if the part is already stored as an array in the query builder:
$isMultiple = is_array($this->_dqlParts[$dqlPartName]);
...
if ($append && $isMultiple) {
if (is_array($dqlPart)) {
$key = key($dqlPart);
$this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
} else {
$this->_dqlParts[$dqlPartName][] = $dqlPart;
}
} else {
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
}
And the WHERE clause, unlike most of the other DQL parts, is not initialised to an array:
private $_dqlParts = array(
'select' => array(),
'from' => array(),
'join' => array(),
'set' => array(),
'where' => null,
'groupBy' => array(),
'having' => null,
'orderBy' => array()
);
Now, this looks a bit like it's by design, but Doctrine 2 is fairly new and this bit seems to have been evolving recently. Personally, I'd raise a bug against this behaviour and see what the project people say. If nothing else, you might end up with improved documentation...

Resources