--- /dev/null
+<?php
+
+namespace RedUNIT\Mysql;
+
+use RedUNIT\Mysql as Mysql;
+use RedBeanPHP\Facade as R;
+
+/**
+ * Foreignkeys
+ *
+ * Tests creation and validity of foreign keys,
+ * foreign key constraints and indexes in Mysql/MariaDB.
+ * Also tests whether the correct contraint action has been selected.
+ *
+ * @file RedUNIT/Mysql/Foreignkeys.php
+ * @desc Tests creation of foreign keys.
+ * @author Gabor de Mooij and the RedBeanPHP Community
+ * @license New BSD/GPLv2
+ *
+ * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
+ * This source file is subject to the New BSD/GPLv2 License that is bundled
+ * with this source code in the file license.txt.
+ */
+class Foreignkeys extends Mysql
+{
+ /**
+ * Test whether we can use foreign keys with keywords.
+ *
+ * @return void
+ */
+ public function testKWConflicts()
+ {
+ R::nuke();
+ $metrics = R::dispense( 'metrics' );
+ $constraint = R::dispense( 'constraint' );
+ $constraint->xownMetrics[] = $metrics;
+ R::store( $constraint );
+ asrt( 1, R::count( 'metrics' ) );
+ R::trash($constraint);
+ asrt( 0, R::count( 'metrics') );
+ }
+
+ /**
+ * Basic FK tests.
+ *
+ * @return void
+ */
+ public function testFKS()
+ {
+ $book = R::dispense( 'book' );
+ $page = R::dispense( 'page' );
+ $cover = R::dispense( 'cover' );
+
+ list( $g1, $g2 ) = R::dispense( 'genre', 2 );
+
+ $g1->name = '1';
+ $g2->name = '2';
+
+ $book->ownPage = array( $page );
+
+ $book->cover = $cover;
+
+ $book->sharedGenre = array( $g1, $g2 );
+
+ R::store( $book );
+
+ $fkbook = R::getAll( 'describe book' );
+ $fkgenre = R::getAll( 'describe book_genre' );
+ $fkpage = R::getAll( 'describe cover' );
+
+ $j = json_encode( R::getAll( 'SELECT
+ ke.referenced_table_name parent,
+ ke.table_name child,
+ ke.constraint_name
+ FROM
+ information_schema.KEY_COLUMN_USAGE ke
+ WHERE
+ ke.referenced_table_name IS NOT NULL
+ AND ke.CONSTRAINT_SCHEMA="oodb"
+ ORDER BY
+ constraint_name;' ) );
+
+ $json = '[
+ {
+ "parent": "genre",
+ "child": "book_genre",
+ "constraint_name": "c_fk_book_genre_genre_id"
+ },
+ {
+ "parent": "book",
+ "child": "book_genre",
+ "constraint_name": "c_fk_book_genre_book_id"
+ },
+ {
+ "parent": "cover",
+ "child": "book",
+ "constraint_name": "c_fk_book_cover_id"
+ },
+ {
+ "parent": "book",
+ "child": "page",
+ "constraint_name": "c_fk_page_book_id"
+ }
+ ]';
+
+ $j1 = json_decode( $j, TRUE );
+
+ $j2 = json_decode( $json, TRUE );
+
+ foreach ( $j1 as $jrow ) {
+ $s = json_encode( $jrow );
+
+ $found = 0;
+
+ foreach ( $j2 as $k => $j2row ) {
+
+ if ( json_encode( $j2row ) === $s ) {
+ pass();
+
+ unset( $j2[$k] );
+
+ $found = 1;
+ break;
+ }
+ }
+
+ if ( !$found ) fail();
+ }
+ }
+
+ /**
+ * Test widen for constraint.
+ *
+ * @return void
+ */
+ public function testWideningColumnForConstraint()
+ {
+ testpack( 'widening column for constraint' );
+
+ $bean1 = R::dispense( 'project' );
+ $bean2 = R::dispense( 'invoice' );
+
+ $bean3 = R::getRedBean()->dispense( 'invoice_project' );
+
+ $bean3->project_id = false;
+ $bean3->invoice_id = true;
+
+ R::store( $bean3 );
+
+ $cols = R::getColumns( 'invoice_project' );
+
+ asrt( $cols['project_id'], "int(11) unsigned" );
+ asrt( $cols['invoice_id'], "int(11) unsigned" );
+ }
+
+ /**
+ * Test adding of constraints directly by invoking
+ * the writer method.
+ *
+ * @return void
+ */
+ public function testContrain()
+ {
+ R::nuke();
+
+ $sql = '
+ CREATE TABLE book (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $sql = '
+ CREATE TABLE page (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $sql = '
+ CREATE TABLE book_page (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ book_id INT( 11 ) UNSIGNED NOT NULL,
+ page_id INT( 11 ) UNSIGNED NOT NULL,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "book_page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 0 );
+
+ $writer = R::getWriter();
+
+ $writer->addFK( 'book_page', 'book', 'book_id', 'id', TRUE );
+ $writer->addFK( 'book_page', 'page', 'page_id', 'id', TRUE );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "book_page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 2 );
+
+ $writer->addFK( 'book_page', 'book', 'book_id', 'id', TRUE );
+ $writer->addFK( 'book_page', 'page', 'page_id', 'id', TRUE );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "book_page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 2 );
+ }
+
+ /**
+ * Test adding foreign keys.
+ *
+ * @return void
+ */
+ public function testAddingForeignKey()
+ {
+ R::nuke();
+
+ $sql = '
+ CREATE TABLE book (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $sql = '
+ CREATE TABLE page (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ book_id INT( 11 ) UNSIGNED NOT NULL,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 0 );
+
+ $writer = R::getWriter();
+
+ //Can we add a foreign key with cascade?
+ $writer->addFK('page', 'book', 'book_id', 'id', TRUE);
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 1 );
+
+ //dont add it twice
+ $writer->addFK('page', 'book', 'book_id', 'id', TRUE);
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 1 );
+
+ //even if different
+ $writer->addFK('page', 'book', 'book_id', 'id', FALSE);
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" ');
+
+ asrt( (int) $numOfFKS, 1 );
+
+ //Now add non-dep key
+ R::nuke();
+
+ $sql = '
+ CREATE TABLE book (
+ id INT( 11 ) UNSIGNED NULL AUTO_INCREMENT,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $sql = '
+ CREATE TABLE page (
+ id INT( 11 ) UNSIGNED AUTO_INCREMENT,
+ book_id INT( 11 ) UNSIGNED NULL,
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 0 );
+
+ //even if different
+ $writer->addFK('page', 'book', 'book_id', 'id', FALSE);
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "CASCADE"');
+
+ asrt( (int) $numOfFKS, 0 );
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" AND DELETE_RULE = "SET NULL"');
+
+ asrt( (int) $numOfFKS, 1 );
+
+ $writer->addFK('page', 'book', 'book_id', 'id', TRUE);
+
+ $numOfFKS = R::getCell('
+ SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS
+ WHERE TABLE_NAME = "page" ');
+ }
+
+ /**
+ * Test whether we can manually create indexes.
+ *
+ * @return void
+ */
+ public function testAddingIndex()
+ {
+ R::nuke();
+
+ $sql = '
+ CREATE TABLE song (
+ id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT,
+ album_id INT( 11 ) UNSIGNED NOT NULL,
+ category VARCHAR( 255 ),
+ PRIMARY KEY ( id )
+ )
+ ENGINE = InnoDB
+ ';
+
+ R::exec( $sql );
+
+ $sql = 'SHOW INDEX FROM song';
+
+ $indexes = R::getAll( $sql );
+
+ asrt( count( $indexes ), 1 );
+ asrt( $indexes[0]['Table'], 'song' );
+ asrt( $indexes[0]['Key_name'], 'PRIMARY' );
+
+ $writer = R::getWriter();
+
+ $writer->addIndex('song', 'index1', 'album_id');
+
+ $indexes = R::getAll( 'SHOW INDEX FROM song' );
+
+ asrt( count( $indexes ), 2 );
+ asrt( $indexes[0]['Table'], 'song' );
+ asrt( $indexes[0]['Key_name'], 'PRIMARY' );
+ asrt( $indexes[1]['Table'], 'song' );
+ asrt( $indexes[1]['Key_name'], 'index1' );
+
+ //Cant add the same index twice
+ $writer->addIndex('song', 'index2', 'category');
+
+ $indexes = R::getAll( 'SHOW INDEX FROM song' );
+
+ asrt( count( $indexes ), 3 );
+
+ //Dont fail, just dont
+ try {
+ $writer->addIndex('song', 'index3', 'nonexistant');
+ pass();
+ } catch( \Exception $e ) {
+ fail();
+ }
+
+ asrt( count( $indexes ), 3 );
+
+ try {
+ $writer->addIndex('nonexistant', 'index4', 'nonexistant');
+ pass();
+ } catch( \Exception $e ) {
+ fail();
+ }
+
+ asrt( count( $indexes ), 3 );
+
+ try {
+ $writer->addIndex('nonexistant', '', 'nonexistant');
+ pass();
+ } catch( \Exception $e ) {
+ fail();
+ }
+
+ asrt( count( $indexes ), 3 );
+ }
+}