I dont recall exactly how, but I do remember being able to add one to an integer column in mysql with a query without knowing the value of that integer column...
something like
update table set column=column+1 where id=1
I am wondering, how can I do the above query with CakePHP 3
You can use \Cake\ORM\Table::updateAll() (or the underlying \Cake\Database\Query::update()), and use an expression to generate the arithmetic operation part, something like:
$additionExpression = $Table->query()->newExpr('column + 1');
$affectedRows = $Table->updateAll(
['column' => $additionExpression],
['id' => 1]
);
Or a little more complex in case you want to make use of automatic identifier quoting:
$additionExpression = $Table
->query()
->newExpr()
->add([
new \Cake\Database\Expression\IdentifierExpression('column'),
'1'
])
->setConjunction('+'); // tieWith() before CakePHP 3.4
See also
Cookbook > Database Access & ORM > Saving Data > Bulk Updates
Cookbook > Database Access & ORM > Query Builder > Raw Expressions
Related
First all, sorry about my english!
I'm trying do a dynamic select in CakePHP when i need change the statements of search. I know can use a variable to do that, but i think about SQL injection. It has other way to do this ?
Example:
$var = "quantidade";//$var can be other values.
$query->find('')->select('quantidade' => $var);
Unlike in other places like Query::where() when using the key => value syntax, there are no SQL injection prevention mechanisms in Query::select(), values passed to this method are being inserted into the query as is (they might get quoted, but not escaped), so you have to take care of securing this yourself.
I'd suggest to use a whitelist, either a hardcoded one:
$allowedFields = [
'field_a',
'field_b'
];
if (in_array($var, $allowedFields, true)) {
$query = $Table
->find()
->select([
'alias' => $var
]);
}
or one retrieved from the tables schema in case you want to allow all fields, but be careful, only do this if it's definitely safe to expose all possible columns to the user!
$allowedFields = $Table->getSchema()->columns(); // use schema() in CakePHP < 3.4
See also
Cookbook > Database Access & ORM > Query Builder > SQL Injection Prevention
Cookbook > Database Access & ORM > Schema System
API > \Cake\ORM\Table::getSchema()
API > \Cake\ORM\Table::schema()
I'm trying to create a query that returns the sum of a column using a case (it has logged time and the format in either minutes or hours, if it's in hours, multiply by 60 to convert to minutes). I'm very close, however the query is not populating the ELSE part of the CASE.
The finder method is:
public function findWithTotalTime(Query $query, array $options)
{
$conversionCase = $query->newExpr()
->addCase(
$query->newExpr()->add(['Times.time' => 'hours']),
['Times.time*60', 'Times.time'],
['integer', 'integer']
);
return $query->join([
'table' => 'times',
'alias' => 'Times',
'type' => 'LEFT',
'conditions' => 'Times.category_id = Categories.id'
])->select([
'Categories.name',
'total' => $query->func()->sum($conversionCase)
])->group('Categories.name');
}
The resulting query is:
SELECT Categories.name AS `Categories__name`, (SUM((CASE WHEN
Times.time = :c0 THEN :c1 END))) AS `total` FROM categories Categories
LEFT JOIN times Times ON Times.category_id = Categories.id GROUP BY
Categories.name
It's missing the ELSE statement before the CASE end, which according to the API docs:
...the last $value is used as the ELSE value...
https://api.cakephp.org/3.3/class-Cake.Database.Expression.QueryExpression.html
I know there might be a better way to do this, but at this point I'd like to at least know how to do CASE statements properly using the built in QueryBuilder.
Both arguments must be arrays
Looks like there are some documenation issues in the Cookbook, and the API could maybe be a little more clear on that subject too. Both, the $conditions argument as well as the $values argument must be arrays in order for this to work.
Enforcing types ends up with casting values
Also you're passing the SQL expression wrong, including the wrong types, defining the types as integer will cause the data passed in $values to be casted to these types, which means that you will be left with 0s.
The syntax that you're using is useful when dealing with user input, which needs to be passed safely. In your case however you want to pass hardcoded identifiers, so what you have to do is to use the key => value syntax to pass the values as literals or identifiers. That would look something like:
'Times.time' => 'identifier'
However, unfortunately there seems to be a bug (or at least an undocumented limitation) which causes the else part to not recognize this syntax properly, so for now you'd have to use the manual way, that is by passing proper expression objects, which btw, you may should have done for the Times.time*60 anyways, as it would otherwise break in case automatic identifier quoting is being applied/required.
tl;dr, Example time
Here's a complete example with all forementioned techniques:
use Cake\Database\Expression\IdentifierExpression;
// ...
$conversionCase = $query
->newExpr()
->addCase(
[
$query->newExpr()->add(['Times.time' => 'hours'])
],
[
$query
->newExpr(new IdentifierExpression('Times.time'))
->add('60')
->tieWith('*'), // setConjunction() as of 3.4.0
new IdentifierExpression('Times.time')
],
);
If you were for sure that you'd never ever make use of automatic identifier quoting, then you could just pass the multiplication fragment as:
'Times.time * 60' => 'literal'
or:
$query->newExpr('Times.time * 60')
See also
Cookbook > Database Access & ORM > Query Builder > Case statements
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions
API > \Cake\Database\Expression\QueryExpression::add()
API > \Cake\Database\Expression\QueryExpression::tieWith()
I am beginner in Jasper report, i have created a jasper report table which contains more than one million data. I want to display the jasper report as per the user need.I have database in PostgreSQL named "Banke" and table named Arsenic_Test.i want to make a choice for my user to select the data, For example I have 3 options for the user to select "data with value X<50, X=50 or X>50. How to do this?
You need to use parameters in your queries.
For example your query can look like
SELECT * FROM some_table WHERE X < $P{value_lower}
Ofcourse you have to prepare more complicated query. I think you need a 3 parameters - one for <, another for = and the last for > and check them if they are not nulls in where cluase (because you can't provide an operation with parameter but just only the value). For example you can use something like this
SELECT *
FROM some_table
WHERE ($P{value_lower} is not null and X < $P{value_lower})
OR ($P{value_equal} is not null and X = $P{value_equal})
OR ($P{value_higher} is not null and X > $P{value_higher})
If you need more information about using parameters then look at for example this tutorial or if you want to learn more about connect JR with your site then you should look at http://community.jaspersoft.com/wiki/php-client-sample-code (specially at Reporting Services section). For example if you want know how to provide inputs then you should look at this example
$controls = array(
'Country_multi_select' => array('USA', 'Mexico'),
'Cascading_state_multi_select' => array('CA', 'OR')
);
$report = $c->reportService()->runReport('/reports/samples/Cascading_multi_select_report', 'html', null, null, $controls);
echo $report;
I'm working on cakePHP 3. I have a user defined function(UDF or Routine) in mysql database. That function takes a parameter and returns an integer value. I have to order that returned value in MySQL order clause.
I know mysql query to use that function. i.e,
SELECT customer_id FROM table_name ORDER BY routine_name(param1); //param1 is 'customer_id' which I have written after SELECT
But I don't know that how to build this query in cakePHP 3. If anyone knows the solution, answer will be appreciate.
Here is my cakePHP 3 code.
$purchasesTable = TableRegistry::get("Purchases");
$query = $purchasesTable->find();
$sf_val = $query->func()->routine_name(['Purchases.customer_id' => 'literal']);
$query->select();
$query->order(
// Routine/Function call should be here as per MySQL query.
// So, I think here I have to do something.
);
I'd suggest that you have a closer look at the (API) docs, it's all mentioned there. You can pass expression objects to Query::order(), and in case you need to specify the direction, there's also Query::orderAsc() and Query::orderDesc().
So
$expression = $query->func()->routine_name(['Purchases.customer_id' => 'literal']);
$query->order($expression);
or
$query->orderAsc($expression);
or
$query->orderDesc($expression);
See also
Cookbook > Database Access & ORM > Query Builder > Selecting Data
API > \Cake\Database\Query::order()
API > \Cake\Database\Query::orderAsc()
API > \Cake\Database\Query::orderDesc()
The question has some answers on SO, but non of them seems to help accomplish, actually, a simple task.
I need to update multiple rows based on condition in one query, using Doctrine2 QueryBuilder. Most obvious way is supposed to be wrong:
$userAgeList = [
'user_name_a' => 30,
'user_name_b' => 40,
'user_name_c' => 50,
];
//Array of found `User` objects from database
$usersList = $this->getUsersList();
foreach($usersList as $user)
{
$userName = $user->getName();
$user->setAge($userAgeList[$userName]);
$this->_manager->persist($user);
}
$this->_manager->flush();
It will create an update query for each User object in transaction, but I need only one query. This source suggests that instead you should rely on the UPDATE query, because in case of it we only execute one SQL UPDATE statement, so I've made like this:
$userAgeList = [
'user_name_a' => 30,
'user_name_b' => 40,
'user_name_c' => 50,
];
$builder = $this->_manager->getRepository(self::REPOSITORY_USER)
->createQueryBuilder(self::REPOSITORY_USER);
foreach($userAgeList as $userName => $age)
{
$builder->update(self::REPOSITORY_USER, 'user')
->set('user.age', $builder->expr()->literal($age))
->where('user.name = :name')
->setParameter('name', $userName)
->getQuery()->execute();
}
But that also makes (obviously) a bunch of updates instead of one. If I assign result of getQuery() to a variable, and try to execute() it after foreach loop, I see that $query understands and accumulates a set(), but it does no such thing for WHERE condition.
Is there any way to accomplish such task in QueryBuilder?
UPDATE - similar questions:
Multiple update queries in doctrine and symfony2 - this one does not assume UPDATE in one query;
Symfony - update multiple records - this also says, qoute, 'one select - one update';
Update multiple columns with Doctrine in Symfony - WHERE condition in this query is always the same, not my case;
Doctrine 2: Update query with query builder - same thing as previous, only one WHERE clause;
http://doctrine-orm.readthedocs.org/en/latest/reference/batch-processing.html - doctrine batch processing does not mention conditions at all...
In MySQL I used to do it using CASE-THEN, but that's not supported by Doctrine.