| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 3.0.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Test\TestCase\ORM\Association;
- use Cake\Database\Expression\IdentifierExpression;
- use Cake\Database\Expression\QueryExpression;
- use Cake\Database\TypeMap;
- use Cake\ORM\Association\BelongsTo;
- use Cake\ORM\Entity;
- use Cake\TestSuite\TestCase;
- /**
- * Tests BelongsTo class
- */
- class BelongsToTest extends TestCase
- {
- /**
- * Fixtures to use.
- *
- * @var array
- */
- public $fixtures = ['core.Articles', 'core.Authors', 'core.Comments'];
- /**
- * Set up
- *
- * @return void
- */
- public function setUp()
- {
- parent::setUp();
- $this->company = $this->getTableLocator()->get('Companies', [
- 'schema' => [
- 'id' => ['type' => 'integer'],
- 'company_name' => ['type' => 'string'],
- '_constraints' => [
- 'primary' => ['type' => 'primary', 'columns' => ['id']],
- ],
- ],
- ]);
- $this->client = $this->getTableLocator()->get('Clients', [
- 'schema' => [
- 'id' => ['type' => 'integer'],
- 'client_name' => ['type' => 'string'],
- 'company_id' => ['type' => 'integer'],
- '_constraints' => [
- 'primary' => ['type' => 'primary', 'columns' => ['id']],
- ],
- ],
- ]);
- $this->companiesTypeMap = new TypeMap([
- 'Companies.id' => 'integer',
- 'id' => 'integer',
- 'Companies.company_name' => 'string',
- 'company_name' => 'string',
- 'Companies__id' => 'integer',
- 'Companies__company_name' => 'string',
- ]);
- }
- /**
- * Test that foreignKey generation
- *
- * @return void
- */
- public function testSetForeignKey()
- {
- $assoc = new BelongsTo('Companies', [
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- ]);
- $this->assertEquals('company_id', $assoc->getForeignKey());
- $this->assertSame($assoc, $assoc->setForeignKey('another_key'));
- $this->assertEquals('another_key', $assoc->getForeignKey());
- }
- /**
- * Test that foreignKey generation
- *
- * @group deprecated
- * @return void
- */
- public function testForeignKey()
- {
- $this->deprecated(function () {
- $assoc = new BelongsTo('Companies', [
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- ]);
- $this->assertEquals('company_id', $assoc->foreignKey());
- $this->assertEquals('another_key', $assoc->foreignKey('another_key'));
- $this->assertEquals('another_key', $assoc->foreignKey());
- });
- }
- /**
- * Test that foreignKey generation ignores database names in target table.
- *
- * @return void
- */
- public function testForeignKeyIgnoreDatabaseName()
- {
- $this->company->setTable('schema.companies');
- $this->client->setTable('schema.clients');
- $assoc = new BelongsTo('Companies', [
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- ]);
- $this->assertEquals('company_id', $assoc->getForeignKey());
- }
- /**
- * Tests that the association reports it can be joined
- *
- * @return void
- */
- public function testCanBeJoined()
- {
- $assoc = new BelongsTo('Test');
- $this->assertTrue($assoc->canBeJoined());
- }
- /**
- * Tests that the alias set on associations is actually on the Entity
- *
- * @return void
- */
- public function testCustomAlias()
- {
- $table = $this->getTableLocator()->get('Articles', [
- 'className' => 'TestPlugin.Articles',
- ]);
- $table->addAssociations([
- 'belongsTo' => [
- 'FooAuthors' => ['className' => 'TestPlugin.Authors', 'foreignKey' => 'author_id'],
- ],
- ]);
- $article = $table->find()->contain(['FooAuthors'])->first();
- $this->assertTrue(isset($article->foo_author));
- $this->assertEquals($article->foo_author->name, 'mariano');
- $this->assertNull($article->Authors);
- }
- /**
- * Tests that the correct join and fields are attached to a query depending on
- * the association config
- *
- * @return void
- */
- public function testAttachTo()
- {
- $config = [
- 'foreignKey' => 'company_id',
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- 'conditions' => ['Companies.is_active' => true],
- ];
- $association = new BelongsTo('Companies', $config);
- $query = $this->client->query();
- $association->attachTo($query);
- $expected = [
- 'Companies__id' => 'Companies.id',
- 'Companies__company_name' => 'Companies.company_name',
- ];
- $this->assertEquals($expected, $query->clause('select'));
- $expected = [
- 'Companies' => [
- 'alias' => 'Companies',
- 'table' => 'companies',
- 'type' => 'LEFT',
- 'conditions' => new QueryExpression([
- 'Companies.is_active' => true,
- ['Companies.id' => new IdentifierExpression('Clients.company_id')],
- ], $this->companiesTypeMap),
- ],
- ];
- $this->assertEquals($expected, $query->clause('join'));
- $this->assertEquals(
- 'integer',
- $query->getTypeMap()->type('Companies__id'),
- 'Associations should map types.'
- );
- }
- /**
- * Tests that it is possible to avoid fields inclusion for the associated table
- *
- * @return void
- */
- public function testAttachToNoFields()
- {
- $config = [
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- 'conditions' => ['Companies.is_active' => true],
- ];
- $query = $this->client->query();
- $association = new BelongsTo('Companies', $config);
- $association->attachTo($query, ['includeFields' => false]);
- $this->assertEmpty($query->clause('select'), 'no fields should be added.');
- }
- /**
- * Tests that using belongsto with a table having a multi column primary
- * key will work if the foreign key is passed
- *
- * @return void
- */
- public function testAttachToMultiPrimaryKey()
- {
- $this->company->setPrimaryKey(['id', 'tenant_id']);
- $config = [
- 'foreignKey' => ['company_id', 'company_tenant_id'],
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- 'conditions' => ['Companies.is_active' => true],
- ];
- $association = new BelongsTo('Companies', $config);
- $query = $this->client->query();
- $association->attachTo($query);
- $expected = [
- 'Companies__id' => 'Companies.id',
- 'Companies__company_name' => 'Companies.company_name',
- ];
- $this->assertEquals($expected, $query->clause('select'));
- $field1 = new IdentifierExpression('Clients.company_id');
- $field2 = new IdentifierExpression('Clients.company_tenant_id');
- $expected = [
- 'Companies' => [
- 'conditions' => new QueryExpression([
- 'Companies.is_active' => true,
- ['Companies.id' => $field1, 'Companies.tenant_id' => $field2],
- ], $this->companiesTypeMap),
- 'table' => 'companies',
- 'type' => 'LEFT',
- 'alias' => 'Companies',
- ],
- ];
- $this->assertEquals($expected, $query->clause('join'));
- }
- /**
- * Tests that using belongsto with a table having a multi column primary
- * key will work if the foreign key is passed
- *
- * @return void
- */
- public function testAttachToMultiPrimaryKeyMismatch()
- {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('Cannot match provided foreignKey for "Companies", got "(company_id)" but expected foreign key for "(id, tenant_id)"');
- $this->company->setPrimaryKey(['id', 'tenant_id']);
- $query = $this->client->query();
- $config = [
- 'foreignKey' => 'company_id',
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- 'conditions' => ['Companies.is_active' => true],
- ];
- $association = new BelongsTo('Companies', $config);
- $association->attachTo($query);
- }
- /**
- * Test the cascading delete of BelongsTo.
- *
- * @return void
- */
- public function testCascadeDelete()
- {
- $mock = $this->getMockBuilder('Cake\ORM\Table')
- ->disableOriginalConstructor()
- ->getMock();
- $config = [
- 'sourceTable' => $this->client,
- 'targetTable' => $mock,
- ];
- $mock->expects($this->never())
- ->method('find');
- $mock->expects($this->never())
- ->method('delete');
- $association = new BelongsTo('Companies', $config);
- $entity = new Entity(['company_name' => 'CakePHP', 'id' => 1]);
- $this->assertTrue($association->cascadeDelete($entity));
- }
- /**
- * Test that saveAssociated() ignores non entity values.
- *
- * @return void
- */
- public function testSaveAssociatedOnlyEntities()
- {
- $mock = $this->getMockBuilder('Cake\ORM\Table')
- ->setMethods(['saveAssociated'])
- ->disableOriginalConstructor()
- ->getMock();
- $config = [
- 'sourceTable' => $this->client,
- 'targetTable' => $mock,
- ];
- $mock->expects($this->never())
- ->method('saveAssociated');
- $entity = new Entity([
- 'title' => 'A Title',
- 'body' => 'A body',
- 'author' => ['name' => 'Jose'],
- ]);
- $association = new BelongsTo('Authors', $config);
- $result = $association->saveAssociated($entity);
- $this->assertSame($result, $entity);
- $this->assertNull($entity->author_id);
- }
- /**
- * Tests that property is being set using the constructor options.
- *
- * @return void
- */
- public function testPropertyOption()
- {
- $config = ['propertyName' => 'thing_placeholder'];
- $association = new BelongsTo('Thing', $config);
- $this->assertEquals('thing_placeholder', $association->getProperty());
- }
- /**
- * Test that plugin names are omitted from property()
- *
- * @return void
- */
- public function testPropertyNoPlugin()
- {
- $mock = $this->getMockBuilder('Cake\ORM\Table')
- ->disableOriginalConstructor()
- ->getMock();
- $config = [
- 'sourceTable' => $this->client,
- 'targetTable' => $mock,
- ];
- $association = new BelongsTo('Contacts.Companies', $config);
- $this->assertEquals('company', $association->getProperty());
- }
- /**
- * Tests that attaching an association to a query will trigger beforeFind
- * for the target table
- *
- * @return void
- */
- public function testAttachToBeforeFind()
- {
- $config = [
- 'foreignKey' => 'company_id',
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- ];
- $listener = $this->getMockBuilder('stdClass')
- ->setMethods(['__invoke'])
- ->getMock();
- $this->company->getEventManager()->on('Model.beforeFind', $listener);
- $association = new BelongsTo('Companies', $config);
- $listener->expects($this->once())->method('__invoke')
- ->with(
- $this->isInstanceOf('\Cake\Event\Event'),
- $this->isInstanceOf('\Cake\ORM\Query'),
- $this->isInstanceOf('\ArrayObject'),
- false
- );
- $association->attachTo($this->client->query());
- }
- /**
- * Tests that attaching an association to a query will trigger beforeFind
- * for the target table
- *
- * @return void
- */
- public function testAttachToBeforeFindExtraOptions()
- {
- $config = [
- 'foreignKey' => 'company_id',
- 'sourceTable' => $this->client,
- 'targetTable' => $this->company,
- ];
- $listener = $this->getMockBuilder('stdClass')
- ->setMethods(['__invoke'])
- ->getMock();
- $this->company->getEventManager()->on('Model.beforeFind', $listener);
- $association = new BelongsTo('Companies', $config);
- $options = new \ArrayObject(['something' => 'more']);
- $listener->expects($this->once())->method('__invoke')
- ->with(
- $this->isInstanceOf('\Cake\Event\Event'),
- $this->isInstanceOf('\Cake\ORM\Query'),
- $options,
- false
- );
- $query = $this->client->query();
- $association->attachTo($query, ['queryBuilder' => function ($q) {
- return $q->applyOptions(['something' => 'more']);
- }]);
- }
- /**
- * Test that failing to add the foreignKey to the list of fields will throw an
- * exception
- *
- * @return void
- */
- public function testAttachToNoFieldsSelected()
- {
- $articles = $this->getTableLocator()->get('Articles');
- $association = $articles->belongsTo('Authors');
- $query = $articles->find()
- ->select(['Authors.name'])
- ->where(['Articles.id' => 1])
- ->contain('Authors');
- $result = $query->firstOrFail();
- $this->assertNotEmpty($result->author);
- $this->assertSame('mariano', $result->author->name);
- $this->assertSame(['author'], array_keys($result->toArray()), 'No other properties included.');
- }
- /**
- * Test that formatResults in a joined association finder doesn't dirty
- * the root entity.
- *
- * @return void
- */
- public function testAttachToFormatResultsNoDirtyResults()
- {
- $this->setAppNamespace('TestApp');
- $articles = $this->getTableLocator()->get('Articles');
- $articles->belongsTo('Authors')
- ->setFinder('formatted');
- $query = $articles->find()
- ->where(['Articles.id' => 1])
- ->contain('Authors');
- $result = $query->firstOrFail();
- $this->assertNotEmpty($result->author);
- $this->assertNotEmpty($result->author->formatted);
- $this->assertFalse($result->isDirty(), 'Record should be clean as it was pulled from the db.');
- }
- }
|