I have a cakePHP problem - I want to make a update query like this
UPDATE table SET field = field + some_var
and I don't know how to do it...
Can anyone help me?
The only "right" way would be using cake's "atomic query" wrapper methods. In your case that would be "updateAll".
The question is a complete duplicate of a dozen other questions - like Incrementing Cakephp database field by a value
$var = 1;
$this->Article->updateAll(
array('Article.viewed' => 'Article.viewed + ' . $var),
array('Article.id' => $id)
);
This is also in the docs: http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-array-conditions
There are two ways to do an update:
If you are only updating one field you can do:
$this->Model->id = foo;
$this->Model->saveField('field_name', 'field_value');
or, you can do an update using $this->Model->save():
$data = array(
'Model'=>array(
'id'=>foo,
'field_name'=>'field_value',
'another_field_name'=>'another_field_value'
)
);
$this->Model->save($data);
You want to avoid using $this->Model->query() and use CakePHP's built in methods because the build in methods are datasource agnostic (they work the same on MySQL, Oracle, MSSQL etc.)
You can use the callback method beforeSave to implement what you need.
Callback Method: BeforeSave
public function beforeSave($options = array()) {
if (!empty($this->data['table']['field'])){
$this->data['table']['field'] += $this->data['table']['some_var'];
}
return true;
}
I think the best method is using the Model::updateAll(array $fields, array $conditions).
The Model::saveField(string $fieldName, string $fieldValue, $validate = false) this method when you try to update using same primary key it shows cannot replace duplicate key error. And think when one updates they must be using the primary key as a matching value to update value.
Use
$this->Baker->updateAll(
array('Baker.approved' => 'Baker.approved + ' . $some_var),
array('Baker.id' => $someId)
);
For more information see: http://book.cakephp.org/2.0/en/models/saving-your-data.html
Related
In my database there is a content table and when fetching data from this table I would like to append field url to the results, which is based on slug field which is contained in the table. Anyway, I have seen a way to do this in the previous versions of cakephp using behavior for the model of this table and then modifying results in afterFind callback in the behavior class. But in version 3 there is no afterFind callback, and they recommend using mapReduce() method instead in the manual, but this method is poorly explained in the manual and I cant figure out how to achieve this using mapReduce().
After little bit of research I realized that the best way to append the url field field to find results is using formatResults method, So this is what I did in my finders:
$query->formatResults(function (\Cake\Datasource\ResultSetInterface $results) {
return $results->map(function ($row) {
$row['url'] = array(
'controller' => 'content',
'action' => 'view',
$row['slug'],
$row['content_type']['alias']
);
return $row;
});
});
I can create without problem a record slug with the title in cakephp 3.x. Now I want to use that field slug on the URL.
How can I do that?
I try with the typical function view changing $id by $slug...
public function view($slug = null)
{
$noticia = $this->Noticias->get($slug, [
'contain' => ['Categorias', 'Usuarios', 'Etiquetas', 'Fotos']
]);
$this->set('noticia', $noticia);
$this->set('_serialize', ['noticia']);
}
but I have the following error: "Record not found in table "noticias".
Thank you
The get() method can only be used to find record by primary key. Instead, you need to use find():
$noticia = $this->Noticias
->findBySlug($slug)
->contain(['Categorias', 'Usuarios', 'Etiquetas', 'Fotos'])
->firstOrFail();
Mysql codeigniter query is not working properly.
Suppose if mysql table looks like this:
user_id|user_name
1|john
2|alex
3|sam
Here user_name is unique
The following query should return false if user_name=john and user_id=1 and true if say user_name=john and user_id=2.
$this->db->get_where('user', array('user_name' => $name,'user_id !=' => $userid));
But it returns true in the case user_name=john and user_id=1.
Can anyone suggest me an alternative way of querying not equal to.
print($this->db->last_query()) gives:
SELECT * FROM (user) WHERE user_name = 'john' AND user_id != '1'
Why dont you use simple $this->db->query('your query');
Simply try this, Add the desired condition in the where function.
$this -> db -> where('invitee_phone !=', $user_phone);
You can go follwoing way too. It work for me
$total = 5;
$CI = get_instance();
$CI->load->database();
$CI->db->order_by('id','asc');
$topusers = $CI->db->get_where('users',array('user_type != 1 && `status` =' => 1),$total,0);
echo $CI ->db ->last_query();
die;
and if still not work for you can go with #rohit suggest: $this->db->query('your query');
Type 1:
Using ->where("column_name !=",$columnname) is fine for one column.
But if you want to check multi columns, you have to form an array inside where clause.
Like this
$whereArray = array(
"employee_name" => $name,
"employee_id !=" => $id,
);
$this->db->select('*')->from('employee')->where($whereArray);
Type 2:
We can just write exactly what we want inside where.
Like
$thi->db->where(("employee_id =1 AND employee name != 'Gopi') OR designation_name='leader#gopis clan'");
Type 2 is good for working with combining queries, i mean paranthesis "()"
you can follow this code:
$query = $this->db->select('*')->from('employee')->where('user_name', $name)->where('user_id !=', $userid)->get();
$last_query = $this->db->last_query();
$result = $query->result_array();
if you pass $name = 'john' and $userid = '1' then it return empty array.
The problem with using $this->db->query('your query'); is that it is not portable. One of the most important reasons to embrace the query builder methods is so that no matter what database driver you use, CodeIgniter ensures that the syntax is appropriate.
If a bit of discussion was possible, I'd probably like to hear why you need composite primary identifiers in your table and I'd like to see what your table schema looks like. However, I think the time for discussion has long passed.
Effectively, you want to return a boolean result stating the availability of the combination of the username AND the id -- if one is matched, but not both, then true (available).
To achieve this, you will want to search the table for an exact matching row with both qualifying conditions, count the rows, convert that integer to a boolean, then return the opposite value (the syntax is simpler than the explanation).
Consider this clean, direct, and portable one-liner.
return !$this->db->where(['user_name' => $name,'user_id' => $userid])->count_all_results('user');
this will return false if the count is > 0 and true if the count is 0.
I have a query to select all the rows from the hire table and display them in a random order.
DB::table('hire_bikes')->order_by(\DB::raw('RAND()'))->get();
I now want to be able to put
concat(SUBSTRING_INDEX(description, " ",25), "...") AS description
into the SELECT part of the query, so that I can select * from the table and a shortened description.
I know this is possible by running a raw query, but I was hoping to be able to do this using Fluent or at least partial Fluent (like above).
How can I do it?
You can actually use select AS without using DB::raw(). Just pass in an array into the select() method like so:
$event = Events::select(['name AS title', 'description AS content'])->first();
// Or just pass multiple parameters
$event = Events::select('name AS title', 'description AS Content');
$event->title;
$event->content;
I tested it.
Also, I'd suggest against using a DB:raw() query to perform a concatenation of your description field. If you're using an eloquent model, you can use accessors and mutators to perform this for you so if you ever need a limited description, you can simply output it in your view and not have to use the same query every time to get a limited description. For example:
class Book extends Eloquent
{
public function getLimitedDescriptionAttribute()
{
return str_limit($this->attributes['description'], $limit = 100, $end = '...');
}
}
In your view:
#foreach($books as $book)
{{ $book->limited_description }}
#endforeach
Example Output (not accurate to limit):
The description of this book is...
I'd also advise against using the DB facade because it always utilizes your default connection. If you're querying a secondary connection, it won't take this into account unless you actively specify it using:
DB::connection('secondary')->table('hire_bikes')->select(['name as title'])->get();
Just to note, if you use a select AS (name AS title) and you wish to update your the model, you will still have to set the proper attribute name that coincides with your database column.
For example, this will cause an exception because the title column does not exist in your database table:
$event = Events::select('name AS title')->first();
$event->title = 'New name';
$event->save(); // Generates exception, 'title' column does not exist.
You can do this by adding a DB::raw() to a select an array in your fluent query. I tested this locally and it works fine.
DB::table('hire_bikes')
->select(
array(
'title',
'url',
'image',
DB::raw('concat(SUBSTRING_INDEX(description, " ",25),"...") AS description'),
'category'
)
)
->order_by(\DB::raw('RAND()'))
->get();
select(array(DB::raw('latitude as lat'), DB::raw('longitude as lon')))
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!