Yii2 console migration down with a different connection fails - yii2

Am trying to perform console migrations to a different connecton but down fails
in my connection i have
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=pos_db',
....other configs
],
'connection_identifier' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=newdbo',
...other configs
],
In my console i have
public function init()
{
$this->db = 'connection_identifier';
parent::init();
}
public function safeUp()
{
$this->createTable('database_connection_domains', [
'id' => $this->primaryKey(),
'domain'=>$this->text()->notNull(),
'connection_id'=>$this->integer()->notNull(),
'created_at' => $this->integer()->notNull(),
'status'=>$this->integer()->defaultValue(0),
'FOREIGN KEY (connection_id) REFERENCES database_connections (id) ON DELETE RESTRICT ON UPDATE CASCADE',
]);
}
/**
* {#inheritdoc}
*/
public function safeDown()
{
$this->dropTable('database_connection_domains');
}
When i run the up migration the database in correctly created on the newdbo database. The problem comes in during down command where the table is not dropped. How do i make this drop the table.
When i run /yii migrate/fresh am getting an error Base table or view already exists: 1050 Table 'database_connections' already exists which means that the table is not dropped
What am i missing?

./yii migrate/fresh does not use migrations to cleanup database, is uses custom implementation which just deletes all tables in correct order. So your settings for DB component are never used. You need to configure database on command call:
./yii migrate/fresh --db=connection_identifier

Related

CakePHP 3: Best Practice for Temporary SQL Tables

Dear CakePHP 3 developers,
I'd like to use SQL's Temporary Tables in a CakePHP 3.4.13 project for a single run through a script. Going through Cake's documentation, there seems no direct way to tell CakePHP my desire. How would I best go about it, then?
I've prepared a Table in src/Model/Table/TempItemsTable.php:
namespace App\Model\Table;
use Cake\ORM\Table;
class TempItemsTable extends Table
{
public $fields = [
'id' => ['type' => 'integer'],
'con' => ['type' => 'string', 'length' => 255, 'null' => false],
'_constraints' => [
'primary' => ['type' => 'primary', 'columns' => ['id']]
]
];
public function initialize(array $config)
{
// $this->setTable(null);
}
}
The idea to use $fields to tell CakePHP the desired table schema comes from a possibly unrelated documentation for Test Fixtures.
But how do I tell CakePHP not to look for an actual table in the database?
The uncommented line $this->setTable(null); was my poor attempt at that, which is supposedly similiar to the right way in earlier versions of CakePHP, but according to version 3.x documentation, setTable() doesn't accept null, while table() does, but it's deprecated as of 3.4 and also didn't change anything.
Finally, of course, I get this exception as soon as I try to access this "table" in a controller via $temp = TableRegistry::get('TempItems');:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'mydatabase.temp_items' doesn't exist
Help, I'm stuck. :(
There's no need to tell it to not look for the table, actually that's the opposite of what you want to do, given that you eventually want to access it.
The table class should basically be configured as usual, and you should create the temporary database table before the application causes it to be accessed. You can either write the raw table creation SQL manually, or generate it from a \Cake\Database\Schema\TableSchema instance, which supports temporary tables.
You can either explicitly create the schema object:
$schema = new \Cake\Database\Schema\TableSchema('temp_items');
$schema
->addColumn('id', ['type' => 'integer'])
->addColumn('con', ['type' => 'string', 'length' => 255, 'null' => false])
->addConstraint('primary', ['type' => 'primary', 'columns' => ['id']])
->setTemporary(true);
$TableObject->setSchema($schema);
or let the table object generate it, using your fields definition array:
$TableObject->setSchema($TableObject->fields);
$schema = $TableObject->getSchema()->setTemporary(true);
You can then generate the table creation SQL from the schema object and run it against the database:
$connection = $TableObject->getConnection();
$queries = $schema->createSql($connection);
$connection->transactional(
function (\Cake\Database\Connection $connection) use ($queries) {
foreach ($queries as $query) {
$stmt = $connection->execute($query);
$stmt->closeCursor();
}
}
);
$queries would be an array of SQL commands required to create the table, something along the lines of:
[
'CREATE TEMPORARY TABLE `temp_items` (
`id` INTEGER AUTO_INCREMENT,
`con` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
)'
]
Note that if you do not assign the schema to the table object, you could run into caching problems, as the cached schema wouldn't match anymore when you change the table definition and do not clear the cache.
See also
Cookbook > Database Access & ORM > Schema System
Cookbook > Database Access & ORM > Database Basics

Select default schema for MS SQL

I need to configure Yii2 to work with Microsoft SQL server.
The db configuration file (db.php) is something like this
return [
'class' => 'yii\db\Connection',
'dsn' => 'sqlsrv:Server=192.168.77.111;Database=xyz',
'username' => 'xy',
'password' => 'xyz',
This works only if i add before the table name in the tableName() function inside all models the correct schema name.
For example:
public static function tableName()
{
return '{{%xyzschema.users}}';
}
How can i set the db configuration so xyzschema is always added when connecting to the table?
I tried with tablePrefix and schemaMap with defaultSchema but it doesn't work
The error returned is
Invalid object name 'users'.
or
Invalid object name 'xyzschema.users'.
If i add tablePrefix to db.php
Update: The defaultSchema property inside connection's schemaMap/Schema config array gets ingored
For this case i solved changing the schema to the standard "dbo" for each table.

Yii2-user: How to create admin user in batch mode?

When deploying my application there is of course always an admin user.
How can I create such an admin user as a first user without any interaction ...
... by means of SQL?
... using a Yii2-migration?
Found it. There is an easy way to do this with Yii2 builtin migrations.
In Yii2-user there are some hooks we can use to create users.
This code has to be inserted in a migration. after creating a new migration ./yii migrate/create, preferably after creating initial tables in the database:
use yii\db\Transaction;
use app\models\user\User;
public function safeUp()
{
$transaction = $this->getDb()->beginTransaction();
$user = \Yii::createObject([
'class' => User::className(),
'scenario' => 'create',
'email' => 'admin',
'username' => 'admin#example.com',
'password' => 'mysecret',
]);
if (!$user->insert(false)) {
$transaction->rollBack();
return false;
}
$user->confirm();
$transaction->commit();
}
The skeleton code can be found in ./migrations/....
Don't forget to add database config parameters in ./config/db.php
and the user module in ./config/console.php

Yii2 Gii Table Prefix

I allways setup table prefix - for this post lets say my prefix is abc_.
So in common\config\main-local.php. I have:
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=database',
'username' => 'user',
'password' => 'pwd',
'charset' => 'utf8',
'tablePrefix' => 'abc_',
],
...
I have worked on Yii1 and used gii to generate models.
In this version it generated files like: table.php.
Now I work with Yii2 and learn the differences:
gii generate files like abc_table.php. Yes - I checked "Use Table Prefix".
This is not ok because prefix should be transparent.
Could please anyone tell me what I'm doing wrong?
You may change the model class name AbcTest to Test. For future model generations, check the Use Table Prefix field in the Gii tool. Gii generate correct model like this:
class Test extends \yii\db\ActiveRecord
{
/**
* #inheritdoc
*/
public static function tableName()
{
return '{{%test}}';
}
...
}
In tableName method, it returns '{{%test}}' if you check Use Table Prefix in the Gii tool. If you do not check the Use Table Prefix, this method return 'abc_test' and generated model class will be named as AbcTest.

Multiple database connections and Yii 2.0

I have two databases, and every database has the same table with the same fields, but how do I get all records from all of two databases at the same time in Yii 2.0?
First you need to configure your databases like below:
return [
'components' => [
'db1' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db1name', //maybe other dbms such as psql,...
'username' => 'db1username',
'password' => 'db1password',
],
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db2name', // Maybe other DBMS such as psql (PostgreSQL),...
'username' => 'db2username',
'password' => 'db2password',
],
],
];
Then you can simply:
// To get from db1
Yii::$app->db1->createCommand((new \yii\db\Query)->select('*')->from('tbl_name'))->queryAll()
// To get from db2
Yii::$app->db2->createCommand((new \yii\db\Query)->select('*')->from('tbl_name'))->queryAll()
If you are using an active record model, in your model you can define:
public static function getDb() {
return Yii::$app->db1;
}
//Or db2
public static function getDb() {
return Yii::$app->db2;
}
Then:
If you have set db1 in the getDb() method, the result will be fetched from db1 and so on.
ModelName::find()->select('*')->all();
Just to add:
I followed the answer provided but still got an error:
"Unknown component ID: db"
After some testing, here is what I discovered: The function getDB is only called AFTER a connection is made to db. Therefore, you cannot delete or rename 'db' in the config file. Instead, you need to let the call to 'db' proceed as normal and then override it afterwards.
The solution (for me) was as follows:
In config/web.php add your second database configuration below db as follows:
'db' => require(__DIR__ . '/db.php'),
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=name',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'on afterOpen' => function ($event) {
$event->sender->createCommand("SET time_zone = '+00:00'")->execute();
},
],
DO NOT rename db. Failure to find db will cause an error. You can name db2 whatever you like.
Now in the model, add the following code:
class ModelNameHere extends \yii\db\ActiveRecord {
// add the function below:
public static function getDb() {
return Yii::$app->get('db2'); // second database
}
This will now override the default db configuration.
I hope that helps somebody else.
Note: you can include the configuration for db2 in another file but you cannot include it in the db.php file (obviously). Instead, create a file called db2.php and call it as you do db:
'db' => require(__DIR__ . '/db.php'),
'db2' => require(__DIR__ . '/db2.php'),
Thanks
Our situation is a little more complex, we have a "parent" database which has a table that contains the name of one or more "child" databases.
The reason for this is that the Yii project is instantiated for each of our clients, and the number of child databases depends on the client, also the database names are arbitrary (although following a pattern).
So we override
\yii\db\ActiveRecord
as follows:
class LodgeActiveRecord extends \yii\db\ActiveRecord
{
public static function getDb()
{
$lodgedb = Yii::$app->params['lodgedb'];
if(array_key_exists( $lodgedb, Yii::$app->params['dbs'])) {
return Yii::$app->params['dbs'][ $lodgedb ];
}
$connection = new \yii\db\Connection([
'dsn' => 'mysql:host=localhost;dbname=' . $lodgedb,
'username' => Yii::$app->params['dbuser'],
'password' => Yii::$app->params['dbpasswd'],
'charset' => 'utf8',
]);
$connection->open(); // not sure if this is necessary at this point
Yii::$app->params['dbs'][ $lodgedb ] = $connection;
return $connection;
}
}
Before calling any database function, first set Yii::$app->params['lodgedb'] to the name of the database required:
Yii::$app->params['lodgedb'] = $lodge->dbname; // used by LodgeActiveRecord
Your model classes don't change except they extend from LodgeActiveRecord:
class BookingRooms extends \app\models\LodgeActiveRecord
If you're using schmunk42/yii2-giiant to generate model classes, there is a 'modelDb' property which you can set to use a database component other than 'db'.