I have a question;
Has anyone experienced to migrate a table using CakePHP 3 Migration Tool that when a specific field is an ENUM data type, the migration script automatically converts it into string or text.
How can I avoid it and how can I maintain the data type from ENUM?
Thanks
It depends on the driver in use, since enum is not supported by all database systems. For the MySQL driver, using the enum type will result in the appropriate DDL.
Migration class:
public function up()
{
$table = $this->table('testenum');
$table
->addColumn('enum_column', 'enum', [
'values' => ['one', 'two']
])
->create();
}
DDL:
CREATE TABLE `testenum` (
`enum_column` enum('one','two') NOT NULL,
PRIMARY KEY (`id`)
)
In the Phinx package, enum is present only in the MysqlAdapter.
Related
im using mysql database with EF core .
i use Identity Schema to my existed MySql database , after doing a migration , update database generate this :
"A schema "Identity" has been set for an object of type "CreateTableOperation" with the name of "Role". MySQL does not support the EF Core concept of schemas. Any schema property of any "MigrationOperation" must be null. This behavior can be changed by setting the SchemaBehavior option in the UseMySql call."
error pic
So how to set the schemabehavior option .
With gratitude.
Update your service registrator (Startup.cs) and modify the DB context to set the SchemaBehavior option as one of the following:
// Throw an exception, if a schema is being used. This is the default.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Throw))
// Silently ignore any schema definitions.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Ignore))
// Use the specified translator delegate to translate from an input schema and object name to
// an output object name whenever a schema is being used.
options.UseMySql(myConnectionString, b => b.SchemaBehavior(MySqlSchemaBehavior.Translate,
(schema, entity) => $"{schema ?? "dbo"}_{entity}"))
// Just ignore all schemas:
optionsBuilder
.UseMySql(
connectionString,
serverVersion,
o => o.SchemaBehavior(MySqlSchemaBehavior.Ignore))
// Translate schemas into a table name prefix like `schema_table`:
optionsBuilder
.UseMySql(
connectionString,
serverVersion,
o => o.SchemaBehavior(MySqlSchemaBehavior.Translate, (schema, table) => $"
{schema}_{table}"))
For Dynamic column supported in Maria-DB and in MySQL we have JSON column type. For one of our projects, we should be implementing a database for Maria-DB (not Mysql).
The Dynamic Column is supported using yii2-dynamic-ar package.
how can can override Eloquent orm in Laravel to add dynamic-columns. in the Yii package which added this feature to ActiveRecord this classes can override ActiveRecord class
implementations classes in Yii framework to support in ActiveRecord ORM:
DynamicActiveRecord.php
DynamicActiveQuery.php
I just created package for handling MariaDB dynamic Column using eloquent and query builder.
To install package run this command:
composer require halalsoft/laravel-dynamic-column
You can start using the package by adding the HasDynamicColumn trait and use Dynamic as attribute cast to your models.
An example:
use Illuminate\Database\Eloquent\Model;
use Halalsoft\LaravelDynamicColumn\Dynamic;
use Halalsoft\LaravelDynamicColumn\HasDynamicColumn;
class MyModel extends Model
{
use HasDynamicColumn;
protected $casts
= [
'the_column' => Dynamic::class,
];
}
Now You can use dynamic column like json column using eloquent or query builder:
$modelData = MyModel::find(1);
$columnData = $modelData->the_column;
$columnData['data1'] = 'value';
$columnData['data2'] = 'value2';
$modelData->the_column = $columnData;
$modelData->save();
You can also create data field as array
$newData = MyModel::create([
'other_column' => 'this just another column data',
'the_column' => ['data1'=>'value1','data2'=>'value2']
]);
to update a json field/key you use, you may use the -> operator when calling the update method:
$page->update(['content->data1' => 'value1new']);
or you can still update whole column using normal array:
$page->update(['content' => ['data1'=>'value1new','data2'=>'value2new']]);
You can set as array using other method like updateOrCreate(), firstOrCreate(), etc.
This package also support query builder using:
Model::query()->where('the_column->data1', 'value1')->first();
This package is still new, if any issue or request just go to github issue
You can have a cast defined for the column in the Model class
//Model class
protected $casts = ['my_column' => 'array];
You can define the datatype for the column as text if you want or json, with the cast defined, you will be able to work with the column data as associative array.
There's also a package to add json datatype in migration for mariadb - it may be of help
https://github.com/ybr-nx/laravel-mariadb
Since this mornong i am facing a very big problem. I am using CodeIgniter to develop a website, and GAS ORM for the database.
I have basically two tables. One named "pool", and one named "partners". I am having two associations between these two tables, so I have two foreign keys in my table Partners referencing the table pool.
Pool(#id:integer, name:varchar)
Partners(#id:integer, associated_pool_id=>Pool, futur_associated_pool_id=>Pool).
As I have two references to the same table, I can't name the foreign keys "pool_id". So in my relationships with Gas ORM, I have to specify the names of the columns. I do it, but it doesn't work...
Here is what I do:
class Partner extends ORM {
public $primary_key = 'id';
public $foreign_key = array('\\Model\\Pool' => 'associated_pool_id', '\\Model\\Pool' => 'future_associated_pool_id');
function _init()
{
// Relationship definition
self::$relationships = array(
'associated_pool' => ORM::belongs_to('\\Model\\Pool'),
'future_association_pool' => ORM::belongs_to('\\Model\\Pool'),
);
self::$fields = array(
'id' => ORM::field('auto[11]'),
'name' => ORM::field('char[255]'),
'associated_pool_id' => ORM::field('int[11]'),
'future_associated_pool_id' => ORM::field('int[11]')
);
}
and in my Pool class :
class Pool extends ORM {
public $primary_key = 'id';
function _init()
{
// Relationship definition
self::$relationships = array(
'associated_partner' => ORM::has_many('\\Model\\Partner'),
'future_associated_partner' => ORM::has_many('\\Model\\Partner'),
);
self::$fields = array(
'id' => ORM::field('auto[11]'),
'name' => ORM::field('char[50]'),
);
}
I have a test controller testing if everything is okay:
class Welcome extends CI_Controller {
public function index()
{
$pool = \Model\Pool::find(1);
echo $pool->name;
$partners = $pool->associated_partner();
var_dump($partners);
}
But I have an error saying:
Error Number: 1054
Champ 'partner.pool_id' inconnu dans where clause
SELECT * FROM partner WHERE partner.pool_id IN (1)
I don't know how to specify to Gas ORM that it shouldn't take "pool_id" but "associated_pool_id"....
Thank you for your help!!!!!!!!!!!!
I don't know, if this topic is still up to date and interesting to some of you, but in general, I had the exact same problem.
I decided Gas ORM to be my mapper in combination with CodeIgniter. As my database structure was given and it was not following the table_pk convention of Gas, I had to define a foreign key by myself which shall refer to my custom database foreign key. However, the definition of it had no impact on anything. Like your error above, the mapper was not able to build the right SQL-statement. The statement looked similar to yours:
SELECT * FROM partner WHERE partner.pool_id IN (1)
Well, it seems like Gas ignores the self-defined foreign keys and tries to use the default table_pk convention. This means, it takes the table (in your case: pool) and the primary key (id) by merging it with a underscore character.
I figured out, that the constructor of orm.php handles every primary and foreign key defined within the entities. In line 191, the code calls an if clause combined with the empty function of php. As the primary key is defined always and there is no negation in the statement, it skips the inner part of the clause every time. However, the inner part takes care of the self-defined foreign keys.
Long story short, I added a negation (!) in line 191 of orm.php which leads me to the following code:
if ( ! empty($this->primary_key))
{
if ( ! empty($this->foreign_key))
{
// Validate foreign keys for consistency naming convention recognizer
$foreign_key = array();
foreach($this->foreign_key as $namespace => $fk)
{
$foreign_key[strtolower($namespace)] = $fk;
}
$this->foreign_key = $foreign_key;
}
else
{
// If so far we didnt have any keys yet,
// then hopefully someone is really follow Gas convention
// while he define his entity relationship (yes, YOU!)
foreach ($this->meta->get('entities') as $name => $entity)
{
if ($entity['type'] == 'belongs_to')
{
$child_name = $entity['child'];
$child_instance = new $child_name;
$child_table = $child_instance->table;
$child_key = $child_instance->primary_key;
$this->foreign_key[strtolower($child_name)] = $child_table.'_'.$child_key;
}
}
}
}
Well, this little fix helped me out a lot and I hope some of you can take advantage of this hint as well.
I'm testing CakePHP 3.0-RC1 for possible use on a new project. After installing, configuring and creating two (yes, two) database tables, I ran 'bake all' against both tables.
After dealing with the spurious foreign key references in the model (a foreign key defined as the primary key referencing itself? C'mon, now, Bakers!) I am hitting this error:
Error: Method Cake\ORM\Query::__toString() must not throw an exception
File /srv/www/htdocs/wmcb/cake/vendor/cakephp/cakephp/src/Database/Driver/Mysql.php
Line: 193
Not my code. ;)
The offending table definition:
-- -----------------------------------------------------
-- Table `ISOCountryCodes`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `iso_country_codes` (
`iso_country_code_id` VARCHAR(4) CHARACTER SET utf8 NOT NULL ,
`iso_country_name` VARCHAR(64) CHARACTER SET utf8 NOT NULL ,
`iso_country_name_french` VARCHAR(64) CHARACTER SET utf8 NOT NULL ,
PRIMARY KEY (`iso_country_code_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8
COMMENT = 'Look-up table of ISO 3166-1 Country Names and Codes'
;
The IsoCountryCodesController index method, as generated by bake:
/**
* Index method
*
* #return void
*/
public function index()
{
$this->paginate = [
'contain' => ['IsoCountryCodes']
];
$this->set('isoCountryCodes', $this->paginate($this->IsoCountryCodes));
$this->set('_serialize', ['isoCountryCodes']);
}
And the initialize method from IsoCountryCodesTable.php:
/**
* Initialize method
*
* #param array $config The configuration for the Table.
* #return void
*/
public function initialize(array $config)
{
$this->table('iso_country_codes');
$this->displayField('iso_country_code_id');
$this->primaryKey('iso_country_code_id');
// $this->belongsTo('IsoCountryCodes', ['foreignKey' => iso_country_code_id']);
}
with the reflexive foreign key commented out.
This behaviour holds for both tables.
CakePHP 2.5 works correctly with the same database table definitions.
Remove the 'contain' part in the paginate array. You don't have any association to IsoCountryCodes
The reason you got that result when baking is that bake is a conventions-based too. It can only try doing its best based on the stablished conventions. One of those conventions is that any column ending in _id is a foreignKey to another table.
Another convention is that the primary keys of tables should be named id. When not following the conventions you will need to help bake figuring out or fixing its guessing errors in the code it generates.
Is there a performant way to fetch the list of foreign keys assigned to a MySQL table?
Querying the information schema with
SELECT
`column_name`,
`referenced_table_schema` AS foreign_db,
`referenced_table_name` AS foreign_table,
`referenced_column_name` AS foreign_column
FROM
`information_schema`.`KEY_COLUMN_USAGE`
WHERE
`constraint_schema` = SCHEMA()
AND
`table_name` = 'your-table-name-here'
AND
`referenced_column_name` IS NOT NULL
ORDER BY
`column_name`;
works, but is painfully slow on the versions of MySQL I've tried it with. A bit of research turned up this bug, which seems to indicate it's an ongoing issue without a clear solution. The solutions which are hinted at require reconfiguring or recompiling mysql with a patch, which doesn't work for the project I'm working on.
I realize it's possible to issue the following
SHOW CREATE TABLE table_name;
and get a string representation of a CREATE TABLE statement, which will include the foreign key constraints. However, parsing this string seems like it would be fragile, and I don't have a large corpus of CREATE TABLE statements to test against. (if there's a standard bit of parsing code for this out there, I'd love some links)
I also realize I can list the indexes with the following
SHOW CREATE TABLE table_name;
The list of indexes will include the foreign keys, but there doesn't appear to be a way to determine which of the indexes are foreign keys, and which are "regular" MySQL indexes. Again, some cross referencing with the SHOW CREATE table information could help here, but that brings us back to fragile string parsing.
Any help, or even links to other smart discussions on the issue, would be appreciated.
SequelPro and Magento both utilize the SHOW CREATE TABLE query to load the foreign key information. Magento's implementation is the one I am going to reference since it's both a PHP based system and one that both of us are very familiar with. However, the following code snippets can be applied to any PHP based system.
The parsing is done in the Varien_Db_Adapter_Pdo_Mysql::getForeignKeys() method (the code for this class can be found here) using a relatively simple RegEx:
$createSql = $this->getCreateTable($tableName, $schemaName);
// collect CONSTRAINT
$regExp = '#,\s+CONSTRAINT `([^`]*)` FOREIGN KEY \(`([^`]*)`\) '
. 'REFERENCES (`[^`]*\.)?`([^`]*)` \(`([^`]*)`\)'
. '( ON DELETE (RESTRICT|CASCADE|SET NULL|NO ACTION))?'
. '( ON UPDATE (RESTRICT|CASCADE|SET NULL|NO ACTION))?#';
$matches = array();
preg_match_all($regExp, $createSql, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$ddl[strtoupper($match[1])] = array(
'FK_NAME' => $match[1],
'SCHEMA_NAME' => $schemaName,
'TABLE_NAME' => $tableName,
'COLUMN_NAME' => $match[2],
'REF_SHEMA_NAME' => isset($match[3]) ? $match[3] : $schemaName,
'REF_TABLE_NAME' => $match[4],
'REF_COLUMN_NAME' => $match[5],
'ON_DELETE' => isset($match[6]) ? $match[7] : '',
'ON_UPDATE' => isset($match[8]) ? $match[9] : ''
);
}
In the doc block it describes the resulting array as follows:
/**
* The return value is an associative array keyed by the UPPERCASE foreign key,
* as returned by the RDBMS.
*
* The value of each array element is an associative array
* with the following keys:
*
* FK_NAME => string; original foreign key name
* SCHEMA_NAME => string; name of database or schema
* TABLE_NAME => string;
* COLUMN_NAME => string; column name
* REF_SCHEMA_NAME => string; name of reference database or schema
* REF_TABLE_NAME => string; reference table name
* REF_COLUMN_NAME => string; reference column name
* ON_DELETE => string; action type on delete row
* ON_UPDATE => string; action type on update row
*/
I know it's not exactly what you were asking for since it's using the SHOW CREATE TABLE output, but based on my findings, it seems to be the generally accepted way of doing things.