ZF2 - How do I insert into select? - php

I am trying to perform an insert into select from a table method in zf2.
The following procedure is what I used as an example.
How to perform an INSERT INTO SELECT query in ZF2
My table method is contained in a table class in ZF2 which is a separate table in the db from the tables I am trying to insert into.
public function addCategoryName($val)
{
$data = array (
'CategoryName' => $val);
$this->tableGateway->insert($data);
$id = $this->tableGateway->lastInsertValue;
$on = "categoryid = $id";
$select = new Select('actionitems');
$select->columns(array('actionitemid', 'categoryid'))->join('categories', $on, array('actionitemid', 'categoryid'), 'cross');
$adapterInsert = new \Zend\Db\Adapter\Adapter(array(
'driver' => 'pdo_mysql',
'database' => 'actionitemmanager',
'username' => 'username',
'password' => 'password'
));
$insert = new Insert();
$insert->into('actionitemcategories');
$insert->columns(array('actionitemid', 'categoryid'));
$insert->values($select);
//insert with select
$adapterInsert->insertWith($insert);
}
Neither
$insert->values($select)
or
$insert->select($select)
work...
The first attempt gives an error that the $select must be an array, whereas $insert->select($select) gives an error that select() is not a method of Insert.
How can I get this query to work?

The Zend Framework feature you're referencing was introduced in v2.3:
Zend\Db\Sql\Insert can use a Select object as the value source (INSERT INTO ... SELECT)
I suspect you're using an older version. You can compare Zend\Db\Sql\Insert in v2.2.10 to v2.3.0 to see the different exceptions thrown for invalid arguments.
You should upgrade Zend Framework to v2.3 or greater to use this feature.

Related

In Laravel 8, how to insert data into a configured database in config/database.php?

In config/database.php (Laravel 8) this is configured:
'connections' => [
'my_db' => [
'driver' => 'mysql',
'host' => 'xx.xx.xxx.xxx',
'port' => '3306',
'database' => 'Sqlxxx',
'username' => 'Sqlxxxx',
'password' => 'passxxx',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
],
'other_db' => [
...
],
],
I try to save some data, but it doesn't work.
DB::connection('my_db')->beginTransaction();
$data = [];
$data["A"] = "a";
$data["B"] = "b";
$pdo = DB::connection('my_db')->getPdo();
if ($pdo) {
DB::connection('my_db')
->table('my_table')
->insert($data);
}
DB::connection('my_db')->commit();
I specify that the connection to the DB my_db works, because I can get data from it. I have the impression that it can read data but not save them.
EDIT:
I have multiple connections defined in config/database.php
my_db is a database outside of my project
There is no error message; just a blank page (APP_DEBUG is set to true and APP_ENV to "local")
I added DB::connection('my_db')->beginTransaction(); to the beginning of the script, to no avail.
It doesn't work in the following way either: DB::connection('my_db')->insert('insert into my_table (A, B) values (?, ?)', ['a', 'b']);
I'm freaking out. Updating works, inserting doesn't. This works: DB::connection('my_db')->table('my_table')->where('id', '1')->update(['a' => '111']);
There are a couple of spots in the documentation on how to do an insert:
The DB Facade's insert() method:
DB::insert('insert into example_table values (col_1, col_2), (?, ?)', ['col_1_value', 'col_2_value']);
This uses a bound parameterized query to directly insert into the default Connection's example_table table. This doesn't appear to be compatible with DB::connection(...)->insert(), as the insert() method used while chaining is not the same method as above, but rather the Builder's method (see below).
The Query Builder's insert() method:
DB::table('example_table')->insert(['col_1' => 'col_1_value', 'col_2' => 'col_2_value']);
The Query Builder in Laravel is a Database-Agnostic wrapper for allowing communication with the database based on the driver (MySQL, PostGres, etc.). It expects a simple associative array representation of the columns being inserted, like ['a' => 'a', 'b' => 'b', ...], and performs the insert based on the supplied or default Connection and specified table (i.e. this is compatible with DB::connection()).
An additional approach would be to use a Model, with the specified Connection defined. For example, let's define an Example.php Model, with the my_db connection:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Example extends Model {
protected $connection = 'my_db';
...
}
Because we're setting protected $connection = 'my_db';, any calls to this model with automatically use the my_db connection defined in config/database.php's connections array. Examples:
$example = Example::create(['a' => 'a', 'b' => 'b']);
This would run INSERT into examples (a, b) VALUES ('a', 'b'); using the correct database syntax (MySQL, Postgres, etc.). Additional calls to Example::update(), Example::delete(), etc. would all know to use the proper connection.
I would recommend this approach over the DB::connection()->table() method, and would highly recommend not using the DB::insert() method at all.
SOLVED: It was my mistake. I was trying to create a new record forgetting to indicate all the NOT NULLABLE ones. I could have figured it out by the fact that the update worked while the insertion did not. I confirm that DB::connection('my_db')->table('my_table')->insert($data); works perfectly.
Since you don't get any errors upon inserting data it seem to be either of two cases:
Your transaction somehow reverts
Your data doesn't get flushed (only cached somewhere in unit of work or something similar)
Try removing the DB::connection('my_db')->beginTransaction(); and DB::connection('my_db')->commit(); statements.

ZF3 Create SELECT query from expression

I want to create a ZF3 \Zend\Db\Sql\Select object where table is pgsql expression:
generate_series('2017-01-01'::date, '2017-02-01'::date, '1 day'::interval)
but if I pass expression as \Zend\Db\Sql\Expression object, like this:
$select = new \Zend\Db\Sql\Select();
$select->from(['dd' => new \Zend\Db\Sql\Expression("generate_series('2017-01-01'::date, '2017-02-01'::date, '1 day'::interval)")]);
I'm getting following error:
Catchable fatal error: Object of class Zend\Db\Sql\Expression could not be converted to string
but if I pass my expression as string, it's getting automatically wrapped and looks like this:
SELECT "dd".* FROM "generate_series('2017-01-01'::date, '2017-02-01'::date, '1 day'::interval)" AS "dd"
which is of course wrong. Is it possible to achieve without overwriting ZF3 Select class?
Select::form() method takes table name as its argument. You may try this way:
$select = new \Zend\Db\Sql\Select();
$select->columns(['AliasName' => new \Zend\Db\Sql\Expression("YourExpression")]);
This would produce following query:
SELECT YourExpression AS "AliasName"
Updated
The working example of the above method is down here. First, create an instance of database adapter providing database information. In this case, we are using PDO driver for Postgresql.
$adapter = new \Zend\Db\Adapter\Adapter([
'driver' => 'Pdo_Pgsql',
'database' => 'YourDatabaseName',
'username' => 'YourDatabaseUsername',
'password' => 'PasswordForDatabase',
]);
Next up, we are going to create an another instance of Sql::class from zend-db component. It is not mandatory if you are using TableGateway::class in your controller action.
$sql = new \Zend\Db\Sql\Sql($adapter);
Now here is the one you want, the Select object, which we are creating from the previous Sql object. Here we are also querying through zend-db's Expression::class to generate some date series.
$select = $sql->select();
$select->columns(["dd" => new \Zend\Db\Sql\Expression("generate_series('2007-02-01'::timestamp, '2007-03-01'::timestamp, '1 day'::interval)")]);
If we output the sql as string we would then get as the following
SELECT generate_series('2007-02-01'::timestamp, '2007-03-01'::timestamp, '1 day'::interval) AS "dd"
As we are using PDO driver for postgresql, we would prepare the statement at the moment, and finally execute the sql.
$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
If we output the results we fetched we would then get a series of dates as the following
foreach ($results as $row) {
print $row['dd'] ."</br>";
}
// Outputs
2007-02-01 00:00:00
2007-02-02 00:00:00
2007-02-03 00:00:00
2007-02-04 00:00:00
...
Hope this would help you!

Update only one field on Cakephp 3

In some part of my app I need to update only the field is_active of some table with a lot of fields. What is the best approach to update only this field and avoid the validations and requiriments of all other fields?
And if you want to update particular row only , use this:
$users= TableRegistry::get('Users');
$user = $users->get($id); // Return article with id = $id (primary_key of row which need to get updated)
$user->is_active = true;
// $user->email= abc#gmail.com; // other fields if necessary
if($users->save($user)){
// saved
} else {
// something went wrong
}
See here (Updating data in CakePHP3).
This will work:
$users = TableRegistry::get('Users');
$query = $users->query();
$query->update()
->set(['is_active' => true])
->where(['id' => $id])
->execute();
http://book.cakephp.org/3.0/en/orm/query-builder.html#updating-data
When you don't want callbacks to be triggered, just use updateAll()
$table->updateAll(['field' => $newValue], ['id' => $entityId]);
Using the example here: http://book.cakephp.org/3.0/en/orm/database-basics.html#running-update-statements. Run the code below to update all records in table_name_here table with a new value for is_active column.
use Cake\Datasource\ConnectionManager;
$connection = ConnectionManager::get('default');
$connection->update('table_name_here', ['is_active' => 'new_value_here']);
I faced this issue when upgrading my project from 2.10 to 3.x.
In 2.10 you could update a single field using:
$this->Menus->saveField('css', $menucss);
But since this method was deprecated, we do as below now, considering that callbacks will not be triggered:
$this->Menus->updateAll(['css' => $menucss], ['id' => $menu_id]);
The other answers don't use internationalization and other models props, callbacks, etc.
I think this is because of the query builder, it does not use the models and so their behaviors, therefore you should use:
$this->loadModel('Inputs');
$input = $this->Inputs->find()->where(['`key`' => $this->request->data['id']])->first();
$this->Inputs->patchEntity($input, ['prop' => $this->request->data['prop']]);
if ($this->Inputs->save($input)) {
die(json_encode(true));
} else {
die(json_encode(false));
}

Log the actual SQL query using ActiveRecord with Yii2?

I'm doing this:
$students = Student::find()->all();
return $this->render('process', array('students' => $students));
and then this in the view:
foreach($students as $student)
{
echo $student->name . ', ';
echo $student->getQuizActivitiesCount(); ?> <br /> <?php
}
i would like to see the sql query being performed. a student "has many" quiz activities, and the query performs perfectly, but i need to see the raw SQL. is this possible?
Method 1
With relations that return yii\db\ActiveQuery instance it's possible to extract the raw SQL query directly in code for example with var_dump().
For example if we have user relation:
/**
* #return \yii\db\ActiveQuery
*/
public function getUser()
{
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
You can then var_dump() the raw SQL like that:
var_dump($model->getUser()->prepare(Yii::$app->db->queryBuilder)->createCommand()->rawSql);
exit();
Note that you should call it like that and not $model->user->... (the latter returns User instance).
But in your case it's not possible because count() immediately returns int. You can var_dump() partial query without count(), but I think it's not convenient.
Note that you can use this method for dumping generated SQL of any ActiveQuery instances (not only those that were returned by relation), for example:
$query = User::find()->where(['status' => User::STATUS_ACTIVE]);
var_dump($query->prepare(Yii::$app->db->queryBuilder)->createCommand()->rawSql);
exit();
Method 2
This is much simpler in my opinion and I personally prefer this one when debugging SQL queries.
Yii 2 has built-in debug module. Just add this to your config:
'modules' => [
'debug' => [
'class' => 'yii\debug\Module',
],
],
Make sure you only have it locally and not on production. If needed, also change allowedIPs property.
This gives you functional panel at the bottom of the page. Find the DB word and click on either count or time. On this page you can view all executed queries and filter them.
I usually don't filter them in Grid and use standard browser search to quickly navigate through and find the necessary query (using the table name as keyword for example).
Method 3
Just make an error in query, for example in column name - cityy instead of city. This will result as database exception and then you can instantly see the generated query in error message.
If you want to log all relational queries of ActiveRecord in console application all proposed methods don't help. They show only main SQL on active record's table, \yii\debug\Module works only in browser.
Alternative method to get all executed SQL queries is to log them by adding specific FileTarget to configuration:
'log' => [
'targets' => [[
...
], [
'class' => 'yii\log\FileTarget',
'logFile' => '#runtime/logs/profile.log',
'logVars' => [],
'levels' => ['profile'],
'categories' => ['yii\db\Command::query'],
'prefix' => function($message) {
return '';
}
]]
]
UPDATE
In order to log insert/update/delete queries one should also add yii\db\Command::execute category:
'categories' => ['yii\db\Command::query', 'yii\db\Command::execute']
you can try this, assume you have a query given like:
$query = new Books::find()->where('author=2');
echo $query->createCommand()->sql;
or to get the SQL with all parameters included try:
$query->createCommand()->getRawSql()
In addition to arogachev answer, when you already work with an ActiveQuery object, here is the line I search to view the rawsql.
/* #var $studentQuery ActiveQuery */
$studentQuery = Student::Find();
// Construct the query as you want it
$studentQuery->where("status=3")->orderBy("grade ASC");
// Get the rawsql
var_dump($studentQuery->prepare(Yii::$app->db->queryBuilder)->createCommand()->rawSql);
// Run the query
$studentQuery->all();
when you have a query object you can also use
$query->createCommand()->getRawSql()
to return the Raw SQL with the parameters included or
$query->createCommand()->sql
which will output the Sql with parameters separately.
In order to log/track every/all queries:
extend \yii\db\Connection and override createCommand method, like below:
namespace app\base;
class Connection extends \yii\db\Connection {
public function createCommand($sql = null, $params = array()) {
$createCommand = parent::createCommand($sql, $params);
$rawSql = $createCommand->getRawSql();
// ########### $rawSql -> LOG IT / OR DO ANYTHING YOU WANT WITH IT
return $createCommand;
}
}
Then, simply change your db connection in your db config like below:
'db' => [
'class' => 'app\base\Connection', // #### HERE
'dsn' => 'pgsql:host=localhost;dbname=dbname',
'username' => 'uname',
'password' => 'pwd',
'charset' => 'utf8',
],
Now, you can track/read/... all queries executed by db connection.
Try like,
$query = Yii::$app->db->createCommand()
->update('table_name', ['title' => 'MyTitle'],['id' => '1']);
var_dump($query->getRawSql()); die();
$query->execute();
Output:
string 'UPDATE `table_name`
SET `title`='MyTitle' WHERE `id`='1'
' (length=204)

Zend Framework Database Insert Issue

I'm starting out using the Zend Framework and have created a suitable model which saves data back to a database table. The issue I am having is that the sql statement is trying to insert '?' as the value for each column in the database. I have created the following save function which passes an array of data to the DBtable adapter functions:
public function save() {
$data = $this->getData();
if ($data['pageId']==0) {
$this->getDbTable()->insert($data);
} else {
$this->getDbTable()->update($data, array('pageId = ?' => $data['pageId']));
}
}
This seems to go through the appropriate motions but the item is not added to the database and the sql statement within MySql logs looks something like:
insert into DB_Table ('pageId','title','body') values ('?', '?', '?');
Not quite sure where this is falling down, any pointers would be gratefully received.
Thanks
Data should be in next format:
$data = array(
'pageId' => 1,
'title' => 'title',
'body' => 'body'
);
Are you sure that $this->getDbTable() returns your db adapter?
Try to use:
$db = new Zend_Db_Table('table_name');
$db->insert($data);

Categories