Cakephp find OR condition not working - mysql

I am looking to display coupons that haven't expired yet and coupons that do not expire at all. Here is my cakephp code:
public function coupons() {
$this->paginate['Coupon']=array(
'limit'=>9,
'order'=>'RAND()',
'OR'=>array(
'expires' =>0,
'Coupon.end_date >'=>date('Y-m-d')
)
);
$c=$this->paginate('Coupon');
$this->set('allcoupons', $c);
}
Everything works correctly EXCEPT for coupons that have expired. They still show up in my results. I have a test record that expires before today, yet it still shows up in my results. Is my 'OR' condition written incorrectly?

never mind. I figured it out. You have to encapsulate the 'or' behind 'conditions', like so:
public function coupons() {
$this->paginate['Coupon']=array(
'limit'=>9,
'order'=>'RAND()',
'conditions'=>array(
'OR'=>array(
'expires' =>0,
'Coupon.end_date >'=>date('Y-m-d')
))
);
$c=$this->paginate('Coupon');
$this->set('allcoupons', $c);
}

Related

Failed DB update requests with Laravel

After spending numerous hours on this and scouring the answers online, I'm royally stuck on a seemingly simple DB update with Laravel. Here is a (simplified) version of the flawed function in one of my controllers:
public function changeDetails(Request $request)
{
...
// It works to change member_id into another...
if (Request::input('recipient_id') != "null") {
DB::table('recipients')
->where('recipients.id','=',Request::input('recipient_id'))
->update([
'recipients.member_id' => Request::input('member_id')
]);
// ...but it won't let me change it to NULL.
} else {
DB::table('recipients')
->where('recipients.id','=',Request::input('recipient_id'))
->update([
'recipients.member_id' => null
]);
};
I initially thought that the issue had to do with the database or table, especially since the 'member_id' is a foreign key. However, I did two tests that prove otherwise. First, I made sure that the column is 'unsigned' and 'nullable'. Second, I manually inserted an integer into the "where" clause instead of the "Request::input('recipient_id')"... and it worked fine. I also confirmed that the value of "Request::input('recipient_id')" is indeed an integer, which should work within the appropriate column (that is 'bigint' type).
Any useful suggestions/observations? They would be greatly appreciated.
The code runs into else statement only when Request::input('recipient_id') is "null",
but your else statement also uses 'recipient_id' which is "null".
So your SQL statement will find a recipients where recipients.id = "null".
Obviously you don't have any "recipient_id" is "null", right?
...
else {
DB::table('recipients')
->where('recipients.id','=',Request::input('recipient_id')) // here the recipient_id is "null"
->update([
'recipients.member_id' => null
]);
};
...

Construct Laravel query using Conditionals

I am trying to create a query based on data sent from a POST request. I got it working when I expect both values in my form to be set, however I would like one of the values to be optional.
In my Controller if I make that call
$year = request()->year;
if(request()->filled('month')){
$month = sprintf("%02d",request()->month);
}
That works for correctly setting $year and $month, but I run in to issues in my query. This is what my query looks like:
$matches = DB::table('games')
->join('clubs', 'games.opposition','=','clubs.id')
->join('stadiums', 'games.stadium','=','stadiums.id')
->select('date','clubs.name AS opposition','games.home/away','stadiums.name')
->whereYear('date',$year)
->whereMonth('date',$month)
->get();
I want that ->whereMonth statement to only be added if the month value is set in the form, so I tried to surround it with
->when(request()->filled('month'),function ($q) use ($month) {
return $q->whereMonth('date',$month);
})
But it's giving me a Undefined variable: month error.
Does anyone have any suggestions? I tried to replace request()->filled('month) with just $month but it gave me the same error.
It is because you don't set month if it's not there but it should work as the conditions are the same. Wouldn't simplifying it help, doing it all in one statement, instead of setting it beforehand. Then you don't have the problem of having instantiated the property.
->when(request()->filled('month'), function ($q) {
return $q->whereMonth('date', sprintf("%02d",request()->month));
})

Laravel keeps getting the same cache result even if I input different search keyword

I got a api request that has a parameter in it which is projectname. The problem is when I search for example A the results will be A but when I search for B the result is till A even if I search C the result is still the same. I think the cache saved the first results from the first search string. My question is how could I save every results in every search query without getting the same result based on the search query?
Here is my code
public function getRecordDetails(Request $request){
if(!empty($request->limit)){
$limit = " LIMIT ".$_REQUEST['limit'];
}
else{
$limit= '';
}
if(empty($request->projectname)){
dd('Field is empty');
}
else{
$data = Cache::rememberForever('results', function () use($request) {
$result = DB::connection('mysql2')
->table('xp_pn_ura_transactions')
->whereRaw(DB::raw("CONCAT(block, ' ', street,' ',project_name,' ', postal_code,'')LIKE '%$request->projectname%' order by STR_TO_DATE(sale_date, '%d-%M-%Y') desc"))
->limit($request->limit)
->distinct()
->get();
$count = DB::connection('mysql2')
->table('xp_pn_ura_transactions')
->whereRaw(DB::raw("CONCAT(block, ' ', street,' ',project_name,' ', postal_code,'')LIKE '%$request->projectname%'"))
->count();
return json_encode(array('count'=>$count,'result'=>$result));
});
return $data;
}
}
PS: This question is based here How could I cache every api response results in my query in Laravel? I answered here but this is different problem based on my answer. Thanks for helping.
Laravel find the Cache by the key. You're using results as your key.
So no matter how different request you pass. It still can find the cache by results.
So it will return the first cache you store in results.
$key = "results:".$request->projectname.':' $request->limit;
Cache::rememberForever($key, function () use ($request) {
...
}
This one will store every different projectname you request.
However
Problem 1:
There are so many diff possibilities that user can request.
I don't think it is a good idea to store all these cache. If there are not that much, it is ok.
Solution:
Or you can use remember() instead of rememberForever()
$ttl = ????; // Find the appropriate time to expire the cache
$value = Cache::remember($key, $ttl, function () {});
Problem 2:
There is a $request->limit in your cache.
That means if someone insert or delete a record in that table. next time you request with another limit, you will face the duplicated records.
Solution:
So I think you can clear the cache after you create , update or delete the records.
Because you are using the same cache slug over and over. You should change the cache slug according to the changed input. Adding the $request as a use argument to your function will not magically change the cache slug.
In your case, this should work:
Cache::rememberForever("results_{$request->projectname}", function () use ($request) {
you should add text value after and before key id like bello
Cache::rememberForever('product_'.$product->id.'_key',function ()
});

Laravel - Eloquent - Filter based on latest HasMany relation

I have this two models, Leads and Status.
class Lead extends Model
{
public function statuses() {
return $this->hasMany('App\LeadStatus', 'lead_id', 'id')
->orderBy('created_at', 'DESC');
}
public function activeStatus() {
return $this->hasOne('App\LeadStatus', 'lead_id', 'id')
->latest();
}
}
class LeadStatus extends Model
{
protected $fillable = ['status', 'lead_id'];
}
This works fine, now I'm trying to get all Leads based on the 'status' of the last LeadStatus.
I've tried a few combinations with no success.
if ($search['status']) {
$builder = $builder
->whereHas('statuses', function($q) use ($search){
$q = $q->latest()->limit(1);
$q->where('status', $search['status']);
});
}
if ($search['status']) {
$builder = $builder
->whereHas('status', function($q) use ($search){
$q = $q->latest()->Where('status', $search['status']);
});
}
Has anybody done this with Eloquent? Do I need to write some raw SQL queries?
EDIT 1: I'll try to explain again :D
In my database, the status of a lead is not a 1 to 1 relation. That is because I want to have a historic list of all the statuses which a Lead has had.
That means that when a Lead is created, the first LeadStatus is created with the status of 'new' and the current date.
If a salesman comes in, he can change the status of the lead, but this DOES NOT update the previous LeadStatus, instead it creates a new related LeadStatus with the current date and status of 'open'.
This way I can see that a Lead was created on 05/05/2018 and that it changed to the status 'open' on 07/05/2018.
Now I'm trying to write a query using eloquent, which only takes in count the LATEST status related to a Lead.
In the previous example, if I filter by Lead with status 'new', this Lead should not appear as it has a status of 'open' by now.
Hope this helps
Try this:
Lead::select('leads.*')
->join('lead_statuses', 'leads.id', 'lead_statuses.lead_id')
->where('lead_statuses.status', $search['status'])
->where('created_at', function($query) {
$query->selectRaw('max(created_at)')
->from('lead_statuses')
->whereColumn('lead_id', 'leads.id');
})->get();
A solution using the primary key (by Borjante):
$builder->where('lead_statuses.id', function($query) {
$query->select('id')
->from('lead_statuses')
->whereColumn('lead_id', 'leads.id')
->orderBy('created_at', 'desc')
->limit(1);
});
I had this same problem and posted my solution here but I think it's worth re-posting as it improves on the re-usability. It's the same idea as the accepted answer but avoids using joins, which can cause issues if you want to eager load relations or use it in a scope.
The first step involves adding a macro to the query Builder in the AppServiceProvider.
use Illuminate\Database\Query\Builder;
Builder::macro('whereLatestRelation', function ($table, $parentRelatedColumn)
{
return $this->where($table . '.id', function ($sub) use ($table, $parentRelatedColumn) {
$sub->select('id')
->from($table . ' AS other')
->whereColumn('other.' . $parentRelatedColumn, $table . '.' . $parentRelatedColumn)
->latest()
->take(1);
});
});
This basically makes the sub-query part of the accepted answer more generic, allowing you to specify the join table and the column they join on. It also uses the latest() function to avoid referencing the created_at column directly. It assumes the other column is an 'id' column, so it can be improved further. To use this you'd then be able to do:
$status = $search['status'];
Lead::whereHas('statuses', function ($q) use ($status) {
$q->where('status', $userId)
->whereLatestRelation((new LeadStatus)->getTable(), 'lead_id');
});
It's the same logic as the accepted answer, but a bit easier to re-use. It will, however, be a little slower, but that should be worth the re-usability.
If I understand it correctly you need / want to get all Leads with a specific status.
So you probably should do something like this:
// In your Modal
public function getLeadById($statusId)
{
return Lead::where('status', $statusId)->get();
// you could of course extend this and do something like this:
// return Lead::where('status', $statusId)->limit()....->get();
}
Basically I am doing a where and returning every lead with a specific id.
You can then use this function in your controller like this:
Lead::getLeadById(1)

Magento JoinLeft() in custom orders grid causing SQL integrity constrain violation for non-admin user in multi-website setup

I have extended the Mage_Adminhtml_Block_Sales_Order_Grid class with a custom module to add several customer attributes (Magento EE 1.10) to the grid.
I added the custom attributes to the collection in my MyCompany_MyModule_Block_Adminhtml_Order_Grid class in the _prepareCollection() method using three joins like this:
protected function _prepareCollection()
{
$collection = Mage::getResourceModel($this->_getCollectionClass());
//get the table names for the customer attributes we'll need
$customerEntityVarchar = Mage::getSingleton('core/resource')
->getTableName('customer_entity_varchar');
$customerEntityInt = Mage::getSingleton('core/resource')
->getTableName('customer_entity_int');
// add left joins to display the necessary customer attribute values
$collection->getSelect()->joinLeft(array(
'customer_entity_int_table'=>$customerEntityInt),
'`main_table`.`customer_id`=`customer_entity_int_table`.`entity_id`
AND `customer_entity_int_table`.`attribute_id`=148',
array('bureau'=>'value'));
$collection->getSelect()->joinLeft(array(
'customer_entity_varchar_table'=>$customerEntityVarchar),
'`main_table`.`customer_id`=`customer_entity_varchar_table`.`entity_id`
AND `customer_entity_varchar_table`.`attribute_id`=149',
array('index_code'=>'value'));
$collection->getSelect()->joinLeft(array(
'customer_entity_varchar_2_table'=>$customerEntityVarchar),
'`main_table`.`customer_id`=`customer_entity_varchar_2_table`.`entity_id`
AND `customer_entity_varchar_2_table`.`attribute_id`=150',
array('did_number'=>'value'));
$this->setCollection($collection);
return parent::_prepareCollection();
}
UPDATE: While everything displays fine when viewing orders, things are not fine when I try to search / filter orders by any of the text join fields (index_code or did_number). The result is a SQL error: "SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'store_id' in where clause is ambiguous."
This problem also exists if I remove all but one of the leftJoin() statements, so something is going wrong with both (either) of the joins with the customer_entity_varchar table.
As now there are two columns with the name store_id, you have to specify filter_index when you add the column to the grid:
$this->addColumn('store_id', array(
...
'filter_index'=>'main_table.store_id',
));
So that it knows which one you are referring while filtering.
I hope it helps!
More than likely it is because you are joining customer_entity_varchar_table twice.
$collection->getSelect()->joinLeft(array(
'customer_entity_varchar_table'=>$customerEntityVarchar),
'`main_table`.`customer_id`=`customer_entity_varchar_table`.`entity_id`
AND `customer_entity_varchar_table`.`attribute_id`=149',
array('index_code'=>'value'));
$collection->getSelect()->joinLeft(array(
'customer_entity_varchar_2_table'=>$customerEntityVarchar),
'`main_table`.`customer_id`=`customer_entity_varchar_2_table`.`entity_id`
AND `customer_entity_varchar_2_table`.`attribute_id`=150',
array('did_number'=>'value'));
You may want to combine those, you can also try and print the SQL to see what the Query looks like:
$collection->getSelect()->getSelectSql();
More info on collections: http://blog.chapagain.com.np/magento-collection-functions/
The problem appears to exist in two different places. One case is if logged in as a user with a single store, the other as a user who can filter various stores.
Single store user
The solution I went with was to override the addAttributeToFilter method on the collection class. Not knowing exactly what changing the Enterprise_AdminGws_Model_Collections::addStoreAttributeToFilter method would affect other behavior I wanted to avoid that, and I found adding a filter index in Mage_Adminhtml_Block_Sales_Order_Grid as Javier suggested did not work.
Instead I added the following method to Mage_Sales_Model_Resource_Order_Grid_Collection:
/**
* {#inheritdoc}
*/
public function addAttributeToFilter($attribute, $condition = null)
{
if (is_string($attribute) && 'store_id' == $attribute) {
$attribute = 'main_table.' . $attribute;
}
return parent::addFieldToFilter($attribute, $condition);
}
A patch can be found here: https://gist.github.com/josephdpurcell/baf93992ff2d941d02c946aeccd48853
Multi-store user
If a user can filter orders by store at admin/sales_order, the following change is also needed to Mage_Adminhtml_Block_Sales_Order_Grid around line 75:
if (!Mage::app()->isSingleStoreMode()) {
$this->addColumn('store_id', array(
'header' => Mage::helper('sales')->__('Purchased From (Store)'),
'index' => 'store_id',
'type' => 'store',
'store_view'=> true,
'display_deleted' => true,
'filter_index' => 'main_table.store_id',
));
}
A patch can be found here: https://gist.github.com/josephdpurcell/c96286a7c4d2f5d1fe92fb36ee5d0d5a
I had the same bug, after grepping the code, I finally found the troublemaker which is in the Enterprise_AdminGws_Model_Collections class at line ~235:
/**
* Add store_id attribute to filter of EAV-collection
*
* #param Mage_Eav_Model_Entity_Collection_Abstract $collection
*/
public function addStoreAttributeToFilter($collection)
{
$collection->addAttributeToFilter('store_id', array('in' => $this->_role->getStoreIds()));
}
You have to replace 'store_id' by 'main_table.store_id', of course you'll have to extend that particular method in your own rewrite to stick into Magento guidelines :p
Hope it helps!