From 20bf8c206d592f0ff238ef1e8e08f2f253253d3f Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 14:08:26 -0600 Subject: [PATCH 1/9] Adds support for CakePHP 3.8+ Signed-off-by: Chris Hallgren --- .gitignore | 1 + .travis.yml | 9 +- README.md | 28 ++-- composer.json | 7 +- phpunit.xml | 31 ++++ src/Model/Behavior/StiBehavior.php | 103 ++++++++++---- src/Plugin.php | 12 ++ .../Model/Behavior/StiBehaviorTest.php | 24 ++-- tests/bootstrap.php | 133 ++++++++++++++---- .../Model/Table/AssistantChefsTable.php | 2 +- tests/test_app/Model/Table/ChefsTable.php | 2 +- tests/test_app/Model/Table/CooksTable.php | 4 +- tests/test_app/Model/Table/UtensilsTable.php | 4 +- 13 files changed, 268 insertions(+), 92 deletions(-) create mode 100644 phpunit.xml create mode 100644 src/Plugin.php diff --git a/.gitignore b/.gitignore index 364f9bd..771e2ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /composer.lock /plugins /vendor +/tmp diff --git a/.travis.yml b/.travis.yml index fffac56..cdda5cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,8 @@ language: php php: - - 5.4 - - 5.5 - 5.6 - - 7 + - 7.4 sudo: false @@ -13,7 +11,6 @@ env: - DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' - DB=sqlite db_dsn='sqlite:///:memory:' - global: - DEFAULT=1 @@ -21,10 +18,10 @@ matrix: fast_finish: true include: - - php: 5.4 + - php: 7.1 env: PHPCS=1 DEFAULT=0 - - php: 5.4 + - php: 7.3 env: COVERALLS=1 DEFAULT=0 DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' before_script: diff --git a/README.md b/README.md index f85bdee..2bd11d9 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ [![Total Downloads](https://img.shields.io/packagist/dt/muffin/sti.svg?style=flat-square)](https://packagist.org/packages/muffin/sti) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) -Single Table Inheritance for CakePHP 3 ORM. +Single Table Inheritance for CakePHP 3.8+ ORM. -> [...] a way to emulate object-oriented inheritance in a relational database. When mapping from a database -> table to an object in an object-oriented language, a field in the database identifies what class in the +> [...] a way to emulate object-oriented inheritance in a relational database. When mapping from a database +> table to an object in an object-oriented language, a field in the database identifies what class in the > hierarchy the object belongs to. (source: [Wikipedia][1]) @@ -27,12 +27,6 @@ You then need to load the plugin. You can use the shell command: bin/cake plugin load Muffin/Sti ``` -or by manually adding statement shown below to `bootstrap.php`: - -```php -Plugin::load('Muffin/Sti'); -``` - ## Usage ```php @@ -56,7 +50,7 @@ class CooksTable extends Table { // Optionally, set the default type. If none is defined, the // first one (i.e. `chef`) will be used. - $this->entityClass('App\Model\Entity\AssistantChef'); + $this->setEntityClass('App\Model\Entity\AssistantChef'); } } ``` @@ -88,7 +82,7 @@ Optionally, you can create classes for your tables that extend the parent table newChef([...]); - + // or, using the parent table again $cooks->newEntity(['type' => 'chef', ...]); - + // or, using the child table $chefs->newEntity([...]); ``` ### Note -For the above examples to work using (*chef), you need to add a custom rule to the `Inflector`: +For the above examples to work using (*chef), you need to add a custom rule to the `Inflector`: ```php Cake\Utility\Inflector::rules('plural', ['/chef$/i' => '\1Chefs']); diff --git a/composer.json b/composer.json index 7c7c567..cbd8c62 100644 --- a/composer.json +++ b/composer.json @@ -32,9 +32,10 @@ "require": { }, "require-dev": { - "cakephp/cakephp": "~3.0", - "cakephp/cakephp-codesniffer": "2.*", - "phpunit/phpunit": "4.1.*" + "php": ">=5.6", + "cakephp/cakephp": "^3.8", + "cakephp/cakephp-codesniffer": "^3.0", + "phpunit/phpunit": "^5|^6" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e34193b --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,31 @@ + + + + + + + + + tests/ + + + + + + + + + + + + + + + src/ + + + + diff --git a/src/Model/Behavior/StiBehavior.php b/src/Model/Behavior/StiBehavior.php index 2cd60b0..edc1550 100644 --- a/src/Model/Behavior/StiBehavior.php +++ b/src/Model/Behavior/StiBehavior.php @@ -21,32 +21,40 @@ class StiBehavior extends Behavior protected $_typeMap = []; + /** + * @param array $config + * + * @throws \Exception + */ public function initialize(array $config) { $this->verifyConfig(); - $defaultEntityClass = $this->_table()->entityClass(); - if ($defaultEntityClass === '\Cake\ORM\Entity') { + $defaultEntityClass = $this->_table()->getEntityClass(); + if ($defaultEntityClass === 'Cake\ORM\Entity') { $defaultEntityClass = current(Hash::extract($this->_typeMap, '{s}.entityClass')); - $this->_table()->entityClass($defaultEntityClass); + $this->_table()->setEntityClass($defaultEntityClass); } if (!method_exists($defaultEntityClass, 'forCopy')) { - throw new \Exception(); + throw new \Exception($defaultEntityClass . ' is not using the StiAwareTrait'); } } + /** + * @throws \Exception + */ public function verifyConfig() { - $config = $this->config(); + $config = $this->getConfig(); $table = $this->_table(); - if (!in_array($config['typeField'], $table->schema()->columns())) { + if (!in_array($config['typeField'], $table->getSchema()->columns())) { throw new \Exception(); } if (!$config['table']) { - $this->config('table', $table->table()); + $this->getConfig('table', $table->getTable()); } foreach ($config['typeMap'] as $key => $entityClass) { @@ -56,6 +64,13 @@ public function verifyConfig() parent::verifyConfig(); } + /** + * @param $method + * @param $args + * + * @return mixed + * @throws \Exception + */ public function __call($method, $args) { $type = Inflector::underscore(substr($method, 3)); @@ -64,10 +79,17 @@ public function __call($method, $args) $args[0] = []; } - $args[0] += [$this->config('typeField') => $type]; + $args[0] += [$this->getConfig('typeField') => $type]; + return call_user_func_array([$this->_table($type), 'newEntity'], $args); } + /** + * @param string|null $key + * + * @return \Cake\ORM\Table + * @throws \Exception + */ protected function _table($key = null) { if ($key === null) { @@ -81,22 +103,28 @@ protected function _table($key = null) $options = $this->_typeMap[$key]; $alias = $options['alias']; - if (TableRegistry::exists($options['alias'])) { + if (TableRegistry::getTableLocator()->exists($options['alias'])) { $options = []; } - return TableRegistry::get($alias, $options); + return TableRegistry::getTableLocator()->get($alias, $options); } + /** + * @param \Cake\Event\Event $event + * @param \Cake\ORM\Query $query + * @param \ArrayObject $options + * @param $primary + */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { - if (!$query->hydrate()) { + if (!$query->isHydrationEnabled()) { return; } $query->formatResults(function ($results) { return $results->map(function ($row) { - $type = $row[$this->config('typeField')]; + $type = $row[$this->getConfig('typeField')]; $entityClass = $this->_typeMap[$type]['entityClass']; return new $entityClass($row->forCopy(), [ 'markNew' => $row->isNew(), @@ -108,13 +136,20 @@ public function beforeFind(Event $event, Query $query, ArrayObject $options, $pr }); } + /** + * @param \Cake\Event\Event $event + * @param \Cake\Validation\Validator $validator + * @param $name + * + * @throws \Exception + */ public function buildValidator(Event $event, Validator $validator, $name) { if ($name !== 'default') { return; } - $class = $event->subject()->entityClass(); + $class = $event->getSubject()->getEntityClass(); $types = array_combine( Hash::extract($this->_typeMap, '{s}.entityClass'), @@ -132,6 +167,13 @@ public function buildValidator(Event $event, Validator $validator, $name) } } + /** + * @param \Cake\Event\Event $event + * @param \Cake\Datasource\EntityInterface $entity + * @param \ArrayObject $options + * + * @throws \Exception + */ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options) { $class = get_class($entity); @@ -144,12 +186,19 @@ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $o throw new \Exception(); } - $entity->set($this->config('typeField'), $types[$class]); + $entity->set($this->getConfig('typeField'), $types[$class]); } + /** + * @param \Cake\Event\Event $event + * @param \ArrayObject $data + * @param \ArrayObject $options + * + * @throws \Exception + */ public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $options) { - $field = $this->config('typeField'); + $field = $this->getConfig('typeField'); if (empty($data[$field])) { return; } @@ -158,25 +207,31 @@ public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $opti throw new \Exception(); } - $this->_table()->entityClass($this->_typeMap[$data[$field]]['entityClass']); + $this->_table()->setEntityClass($this->_typeMap[$data[$field]]['entityClass']); } + /** + * @param $key + * @param $entityClass + * + * @throws \Exception + */ public function addType($key, $entityClass) { list($namespace, $entityName) = explode('\\Entity\\', $entityClass); - $connection = $this->_table->connection(); - $table = $this->config('table'); + $connection = $this->_table->getConnection(); + $table = $this->getConfig('table'); $alias = Inflector::pluralize($entityName); $className = $namespace . '\\Table\\' . $alias . 'Table'; if (!class_exists($className)) { $className = null; } - if (TableRegistry::exists($alias)) { - $existingTable = TableRegistry::get($alias); - if ($table !== $existingTable->table() - || $connection !== $existingTable->connection() - || $entityClass !== $existingTable->entityClass() + if (TableRegistry::getTableLocator()->exists($alias)) { + $existingTable = TableRegistry::getTableLocator()->get($alias); + if ($table !== $existingTable->getTable() + || $connection !== $existingTable->getConnection() + || $entityClass !== $existingTable->getEntityClass() ) { throw new \Exception(); } @@ -186,6 +241,6 @@ public function addType($key, $entityClass) $this->_typeMap[$key] = compact('alias', 'entityClass', 'table', 'connection', 'className'); $method = 'new' . Inflector::classify($entityName); - $this->config('implementedMethods.' . $method, $method); + $this->setConfig('implementedMethods.' . $method, $method); } } diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..832eaba --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,12 @@ +Table = TableRegistry::get('Cooks', ['className' => CooksTable::class]); + $this->Table = TableRegistry::getTableLocator()->get('Cooks', ['className' => CooksTable::class]); } public function tearDown() { parent::tearDown(); - TableRegistry::clear(); + TableRegistry::getTableLocator()->clear(); } public function testSave() @@ -43,23 +48,24 @@ public function testBeforeFind() $results = $this->Table->find()->toArray(); $this->assertInstanceOf('Muffin\Sti\TestApp\Model\Entity\Chef', $results[0]); $this->assertInstanceOf('Muffin\Sti\TestApp\Model\Entity\Baker', $results[1]); - $this->assertEquals('Bakers', $results[1]->source()); + $this->assertEquals('Bakers', $results[1]->getSource()); } public function testValidation() { $expected = ['name' => ['_empty' => 'chef']]; + /** @var \Muffin\Sti\TestApp\Model\Entity\Chef $entity */ $entity = $this->Table->newChef(['name' => null]); - $this->assertEquals($expected, $entity->errors()); + $this->assertEquals($expected, $entity->getErrors()); $entity = $this->Table->newEntity(['name' => null, 'type' => 'chef']); - $this->assertEquals($expected, $entity->errors()); + $this->assertEquals($expected, $entity->getErrors()); - TableRegistry::clear(); - $table = TableRegistry::get('Chefs', ['className' => 'Muffin\Sti\TestApp\Model\Table\ChefsTable']); + TableRegistry::getTableLocator()->clear(); + $table = TableRegistry::getTableLocator()->get('Chefs', ['className' => 'Muffin\Sti\TestApp\Model\Table\ChefsTable']); $entity = $table->newEntity(['name' => null]); - $this->assertEquals($expected, $entity->errors()); + $this->assertEquals($expected, $entity->getErrors()); } public function testFindWithAssociation() @@ -73,7 +79,7 @@ public function testFindWithAssociation() public function testFindWithHydrateFalse() { - $results = $this->Table->find()->contain('Utensils')->hydrate(false)->toArray(); + $results = $this->Table->find()->contain('Utensils')->enableHydration(false)->toArray(); $this->assertTrue(is_array($results[0])); $this->assertTrue(is_array($results[0]['utensils'][0])); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 44356d6..8c78efa 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,31 +1,110 @@ 'App', + 'encoding' => 'UTF-8', + 'paths' => [ + 'templates' => [ROOT . DS . 'tests' . DS . 'test_app' . DS . 'src' . DS . 'Template' . DS], + ], +]); + +Cake\Core\Configure::write('debug', true); + +Cake\Core\Configure::write('EmailTransport', [ + 'default' => [ + 'className' => 'Debug', + ], +]); +Cake\Core\Configure::write('Email', [ + 'default' => [ + 'transport' => 'default', + 'from' => 'you@localhost', + ], +]); + +mb_internal_encoding('UTF-8'); + +$Tmp = new \Cake\Filesystem\Folder(TMP); +$Tmp->create(TMP . 'cache/models', 0770); +$Tmp->create(TMP . 'cache/persistent', 0770); +$Tmp->create(TMP . 'cache/views', 0770); + +$cache = [ + 'default' => [ + 'engine' => 'File', + 'path' => CACHE, + ], + '_cake_core_' => [ + 'className' => 'File', + 'prefix' => 'crud_myapp_cake_core_', + 'path' => CACHE . 'persistent/', + 'serialize' => true, + 'duration' => '+10 seconds', + ], + '_cake_model_' => [ + 'className' => 'File', + 'prefix' => 'crud_my_app_cake_model_', + 'path' => CACHE . 'models/', + 'serialize' => 'File', + 'duration' => '+10 seconds', + ], +]; + +Cake\Cache\Cache::setConfig($cache); + +Cake\Core\Plugin::getCollection()->add(new \Muffin\Sti\Plugin()); + +// Allow local overwrite +// E.g. in your console: export db_dsn="mysql://root:secret@127.0.0.1/cake_test" +if (!getenv('db_class') && getenv('db_dsn')) { + ConnectionManager::setConfig('test', ['url' => getenv('db_dsn')]); + return; +} +if (!getenv('db_class')) { + putenv('db_class=Cake\Database\Driver\Sqlite'); + putenv('db_dsn=sqlite::memory:'); } -require $root . '/vendor/cakephp/cakephp/tests/bootstrap.php'; +// Uses Travis config then (MySQL, Postgres, ...) +ConnectionManager::setConfig('test', [ + 'className' => 'Cake\Database\Connection', + 'driver' => getenv('db_class'), + 'dsn' => getenv('db_dsn'), + 'database' => getenv('db_database'), + 'username' => getenv('db_username'), + 'password' => getenv('db_password'), + 'timezone' => 'UTC', + 'quoteIdentifiers' => true, + 'cacheMetadata' => true, +]); -\Cake\Core\Plugin::load('Muffin/Sti', ['path' => dirname(dirname(__FILE__)) . DS]); -\Cake\Utility\Inflector::rules('plural', ['/chef$/i' => '\1Chefs']); +Cake\Core\Configure::write('debug', true); diff --git a/tests/test_app/Model/Table/AssistantChefsTable.php b/tests/test_app/Model/Table/AssistantChefsTable.php index ad0ef5a..2b4b97f 100644 --- a/tests/test_app/Model/Table/AssistantChefsTable.php +++ b/tests/test_app/Model/Table/AssistantChefsTable.php @@ -8,7 +8,7 @@ class AssistantChefsTable extends CooksTable { public function validationDefault(Validator $validator) { - $validator->notEmpty('name', 'assistant chef'); + $validator->notEmptyString('name', 'assistant chef'); return $validator; } } diff --git a/tests/test_app/Model/Table/ChefsTable.php b/tests/test_app/Model/Table/ChefsTable.php index 165fc59..ca21ac0 100644 --- a/tests/test_app/Model/Table/ChefsTable.php +++ b/tests/test_app/Model/Table/ChefsTable.php @@ -7,7 +7,7 @@ class ChefsTable extends CooksTable { public function validationDefault(Validator $validator) { - $validator->notEmpty('name', 'chef'); + $validator->notEmptyString('name', 'chef'); return $validator; } } diff --git a/tests/test_app/Model/Table/CooksTable.php b/tests/test_app/Model/Table/CooksTable.php index 4dacb03..4946f55 100644 --- a/tests/test_app/Model/Table/CooksTable.php +++ b/tests/test_app/Model/Table/CooksTable.php @@ -8,7 +8,7 @@ class CooksTable extends Table { public function initialize(array $config) { - $this->table('sti_cooks'); + $this->setTable('sti_cooks'); $this->addBehavior('Muffin/Sti.Sti', [ 'typeMap' => [ 'chef' => 'Muffin\Sti\TestApp\Model\Entity\Chef', @@ -23,6 +23,6 @@ public function initialize(array $config) public function validationBaker(Validator $validator) { - return $validator->notEmpty('name', 'baker'); + return $validator->notEmptyString('name', 'baker'); } } diff --git a/tests/test_app/Model/Table/UtensilsTable.php b/tests/test_app/Model/Table/UtensilsTable.php index e8baae0..000c786 100644 --- a/tests/test_app/Model/Table/UtensilsTable.php +++ b/tests/test_app/Model/Table/UtensilsTable.php @@ -8,13 +8,13 @@ class UtensilsTable extends Table { public function initialize(array $config) { - $this->table('sti_utensils'); + $this->setTable('sti_utensils'); $this->addBehavior('Muffin/Sti.Sti', [ 'typeMap' => [ 'spoon' => 'Muffin\Sti\TestApp\Model\Entity\Spoon', 'electronic' => 'Muffin\Sti\TestApp\Model\Entity\Electronic', ] ]); - $this->entityClass('Muffin\Sti\TestApp\Model\Entity\Spoon'); + $this->setEntityClass('Muffin\Sti\TestApp\Model\Entity\Spoon'); } } From 8fcff85728a170078c157e9c365c000d4996a8dc Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 14:25:14 -0600 Subject: [PATCH 2/9] Cs fixes Signed-off-by: Chris Hallgren --- .gitignore | 1 + .travis.yml | 54 +++++++++++++------ composer.json | 15 ++++-- phpcs.xml.dist | 12 +++++ src/Model/Behavior/StiBehavior.php | 7 +-- tests/Fixture/CooksFixture.php | 4 +- tests/Fixture/UtensilsFixture.php | 2 +- tests/bootstrap.php | 1 + tests/test_app/Model/Entity/Chef.php | 1 + .../Model/Table/AssistantChefsTable.php | 1 + tests/test_app/Model/Table/ChefsTable.php | 1 + tests/test_app/Model/Table/CooksTable.php | 2 +- tests/test_app/Model/Table/UtensilsTable.php | 2 +- 13 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 phpcs.xml.dist diff --git a/.gitignore b/.gitignore index 771e2ff..0bcf9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /plugins /vendor /tmp +/phpcs.xml diff --git a/.travis.yml b/.travis.yml index cdda5cc..fa1fb8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,39 +8,63 @@ sudo: false env: matrix: - - DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + - DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test' - DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' - DB=sqlite db_dsn='sqlite:///:memory:' global: - DEFAULT=1 +services: + - postgresql + - mysql + matrix: fast_finish: true include: - php: 7.1 - env: PHPCS=1 DEFAULT=0 + env: DB=pgsql db_dsn='postgres://postgres@127.0.0.1/cakephp_test' + + - php: 7.2 + env: DB=sqlite db_dsn='sqlite:///:memory:' + + - php: 5.6 + env: PREFER_LOWEST=1 - php: 7.3 - env: COVERALLS=1 DEFAULT=0 DB=mysql db_dsn='mysql://travis@0.0.0.0/cakephp_test' + env: CODECOVERAGE=1 DEFAULT=0 DB=mysql db_dsn='mysql://root@127.0.0.1/cakephp_test' -before_script: - - composer self-update - - composer install --prefer-dist --no-interaction + - php: 7.3 + env: CHECKS=1 DEFAULT=0 - - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'CREATE DATABASE cakephp_test;'; fi" - - sh -c "if [ '$DB' = 'pgsql' ]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi" +before_install: + - phpenv config-rm xdebug.ini - - sh -c "if [ '$PHPCS' = '1' ]; then composer require cakephp/cakephp-codesniffer:dev-master; fi" +before_script: + - if [[ $CHECKS != 1 ]]; then composer require --dev phpunit/phpunit:"^5.7.14|^6.0"; fi - - sh -c "if [ '$COVERALLS' = '1' ]; then composer require --dev satooshi/php-coveralls:dev-master; fi" - - sh -c "if [ '$COVERALLS' = '1' ]; then mkdir -p build/logs; fi" + - if [[ $PREFER_LOWEST != 1 ]]; then composer install --prefer-source --no-interaction; fi + - if [[ $PREFER_LOWEST == 1 ]]; then composer update --prefer-lowest --prefer-stable --prefer-dist --no-interaction; fi + - if [[ $PREFER_LOWEST == 1 ]]; then composer require --dev dereuromark/composer-prefer-lowest:dev-master; fi + + - if [[ $DB == 'mysql' ]]; then mysql -u root -e 'CREATE DATABASE cakephp_test;'; fi + - if [[ $DB == 'pgsql' ]]; then psql -c 'CREATE DATABASE cakephp_test;' -U postgres; fi script: - - sh -c "if [ '$DEFAULT' = '1' ]; then phpunit --stderr; fi" - - sh -c "if [ '$PHPCS' = '1' ]; then ./vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests; fi" - - sh -c "if [ '$COVERALLS' = '1' ]; then phpunit --stderr --coverage-clover build/logs/clover.xml; fi" - - sh -c "if [ '$COVERALLS' = '1' ]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi" + - if [[ $DEFAULT == 1 ]]; then vendor/bin/phpunit; fi + - if [[ $PREFER_LOWEST == 1 ]]; then vendor/bin/validate-prefer-lowest; fi + + - if [[ $CHECKS == 1 ]]; then composer phpstan-setup && composer phpstan; fi + - if [[ $CHECKS == 1 ]]; then composer cs-check; fi + + - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml; fi + +after_success: + - if [[ $CODECOVERAGE == 1 ]]; then bash <(curl -s https://codecov.io/bash); fi + +cache: + directories: + - $HOME/.composer/cache notifications: email: false diff --git a/composer.json b/composer.json index cbd8c62..cc96fa9 100644 --- a/composer.json +++ b/composer.json @@ -30,12 +30,11 @@ "source": "https://github.com/usemuffin/sti" }, "require": { + "php": ">=5.6", + "cakephp/cakephp": "^3.8" }, "require-dev": { - "php": ">=5.6", - "cakephp/cakephp": "^3.8", - "cakephp/cakephp-codesniffer": "^3.0", - "phpunit/phpunit": "^5|^6" + "cakephp/cakephp-codesniffer": "^3.0" }, "autoload": { "psr-4": { @@ -48,6 +47,14 @@ "Muffin\\Sti\\TestApp\\": "tests/test_app" } }, + "scripts": { + "phpstan": "phpstan analyse -c tests/phpstan.neon -l 5 src/", + "phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan-shim:^0.11 && mv composer.backup composer.json", + "test": "php phpunit.phar", + "test-setup": "[ ! -f phpunit.phar ] && wget https://phar.phpunit.de/phpunit-6.5.13.phar && mv phpunit-6.5.13.phar phpunit.phar || true", + "cs-check": "phpcs -p --ignore=/config/Migrations/ --extensions=php src/ tests/", + "cs-fix": "phpcbf -v --ignore=/config/Migrations/ --extensions=php src/ tests/" + }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..8f62bcc --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,12 @@ + + + + + + + + + + 0 + + diff --git a/src/Model/Behavior/StiBehavior.php b/src/Model/Behavior/StiBehavior.php index edc1550..a4eb5d3 100644 --- a/src/Model/Behavior/StiBehavior.php +++ b/src/Model/Behavior/StiBehavior.php @@ -16,7 +16,7 @@ class StiBehavior extends Behavior protected $_defaultConfig = [ 'table' => null, 'typeField' => 'type', - 'typeMap' => [] + 'typeMap' => [], ]; protected $_typeMap = []; @@ -126,6 +126,7 @@ public function beforeFind(Event $event, Query $query, ArrayObject $options, $pr return $results->map(function ($row) { $type = $row[$this->getConfig('typeField')]; $entityClass = $this->_typeMap[$type]['entityClass']; + return new $entityClass($row->forCopy(), [ 'markNew' => $row->isNew(), 'markClean' => true, @@ -229,13 +230,13 @@ public function addType($key, $entityClass) if (TableRegistry::getTableLocator()->exists($alias)) { $existingTable = TableRegistry::getTableLocator()->get($alias); - if ($table !== $existingTable->getTable() + if ( + $table !== $existingTable->getTable() || $connection !== $existingTable->getConnection() || $entityClass !== $existingTable->getEntityClass() ) { throw new \Exception(); } - } $this->_typeMap[$key] = compact('alias', 'entityClass', 'table', 'connection', 'className'); diff --git a/tests/Fixture/CooksFixture.php b/tests/Fixture/CooksFixture.php index 5a3a7af..77882d2 100644 --- a/tests/Fixture/CooksFixture.php +++ b/tests/Fixture/CooksFixture.php @@ -14,12 +14,12 @@ class CooksFixture extends TestFixture 'age' => ['type' => 'integer'], '_constraints' => [ 'primary' => ['type' => 'primary', 'columns' => ['id']], - ] + ], ]; public $records = [ ['name' => 'The Chef', 'type' => 'chef', 'age' => 50], ['name' => 'The Baker', 'type' => 'baker', 'age' => 40], - ['name' => 'The Assistant Chef', 'type' => 'assistant_chef', 'age' => 20] + ['name' => 'The Assistant Chef', 'type' => 'assistant_chef', 'age' => 20], ]; } diff --git a/tests/Fixture/UtensilsFixture.php b/tests/Fixture/UtensilsFixture.php index 9501dd3..fcd0abf 100644 --- a/tests/Fixture/UtensilsFixture.php +++ b/tests/Fixture/UtensilsFixture.php @@ -14,7 +14,7 @@ class UtensilsFixture extends TestFixture 'type' => ['type' => 'string'], '_constraints' => [ 'primary' => ['type' => 'primary', 'columns' => ['id']], - ] + ], ]; public $records = [ diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8c78efa..cbd63dc 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -87,6 +87,7 @@ // E.g. in your console: export db_dsn="mysql://root:secret@127.0.0.1/cake_test" if (!getenv('db_class') && getenv('db_dsn')) { ConnectionManager::setConfig('test', ['url' => getenv('db_dsn')]); + return; } if (!getenv('db_class')) { diff --git a/tests/test_app/Model/Entity/Chef.php b/tests/test_app/Model/Entity/Chef.php index 623c62d..a39cf0d 100644 --- a/tests/test_app/Model/Entity/Chef.php +++ b/tests/test_app/Model/Entity/Chef.php @@ -8,6 +8,7 @@ class Chef extends Entity { use StiAwareTrait; + protected $_hidden = [ 'age', ]; diff --git a/tests/test_app/Model/Table/AssistantChefsTable.php b/tests/test_app/Model/Table/AssistantChefsTable.php index 2b4b97f..3ae6a78 100644 --- a/tests/test_app/Model/Table/AssistantChefsTable.php +++ b/tests/test_app/Model/Table/AssistantChefsTable.php @@ -9,6 +9,7 @@ class AssistantChefsTable extends CooksTable public function validationDefault(Validator $validator) { $validator->notEmptyString('name', 'assistant chef'); + return $validator; } } diff --git a/tests/test_app/Model/Table/ChefsTable.php b/tests/test_app/Model/Table/ChefsTable.php index ca21ac0..0e48d6f 100644 --- a/tests/test_app/Model/Table/ChefsTable.php +++ b/tests/test_app/Model/Table/ChefsTable.php @@ -8,6 +8,7 @@ class ChefsTable extends CooksTable public function validationDefault(Validator $validator) { $validator->notEmptyString('name', 'chef'); + return $validator; } } diff --git a/tests/test_app/Model/Table/CooksTable.php b/tests/test_app/Model/Table/CooksTable.php index 4946f55..a74d9c5 100644 --- a/tests/test_app/Model/Table/CooksTable.php +++ b/tests/test_app/Model/Table/CooksTable.php @@ -14,7 +14,7 @@ public function initialize(array $config) 'chef' => 'Muffin\Sti\TestApp\Model\Entity\Chef', 'baker' => 'Muffin\Sti\TestApp\Model\Entity\Baker', 'assistant_chef' => 'Muffin\Sti\TestApp\Model\Entity\AssistantChef', - ] + ], ]); $this->hasMany('Utensils', [ 'className' => UtensilsTable::class, diff --git a/tests/test_app/Model/Table/UtensilsTable.php b/tests/test_app/Model/Table/UtensilsTable.php index 000c786..81d5aa5 100644 --- a/tests/test_app/Model/Table/UtensilsTable.php +++ b/tests/test_app/Model/Table/UtensilsTable.php @@ -13,7 +13,7 @@ public function initialize(array $config) 'typeMap' => [ 'spoon' => 'Muffin\Sti\TestApp\Model\Entity\Spoon', 'electronic' => 'Muffin\Sti\TestApp\Model\Entity\Electronic', - ] + ], ]); $this->setEntityClass('Muffin\Sti\TestApp\Model\Entity\Spoon'); } From e4fab56f5cd890e06d7e6e8081ba3865399269a1 Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 14:53:33 -0600 Subject: [PATCH 3/9] Fixing CS complaints Signed-off-by: Chris Hallgren --- README.md | 2 +- src/Model/Behavior/StiBehavior.php | 67 ++++++++++++++++++++---------- src/Model/Entity/StiAwareTrait.php | 5 +++ 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 2bd11d9..56ea168 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Total Downloads](https://img.shields.io/packagist/dt/muffin/sti.svg?style=flat-square)](https://packagist.org/packages/muffin/sti) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) -Single Table Inheritance for CakePHP 3.8+ ORM. +Single Table Inheritance for CakePHP 3 ORM. > [...] a way to emulate object-oriented inheritance in a relational database. When mapping from a database > table to an object in an object-oriented language, a field in the database identifies what class in the diff --git a/src/Model/Behavior/StiBehavior.php b/src/Model/Behavior/StiBehavior.php index a4eb5d3..fcc68a2 100644 --- a/src/Model/Behavior/StiBehavior.php +++ b/src/Model/Behavior/StiBehavior.php @@ -22,9 +22,12 @@ class StiBehavior extends Behavior protected $_typeMap = []; /** - * @param array $config + * Initialized the Sti Behavior * - * @throws \Exception + * @param array $config Configuration options passed to the constructor + * @return void + * + * @throws \Exception If the Entity isn't using the trait \Muffin\Sti\Model\Entity\StiAwareTrait */ public function initialize(array $config) { @@ -42,6 +45,10 @@ public function initialize(array $config) } /** + * Verifies the configuration of the Behavior + * + * @return void + * * @throws \Exception */ public function verifyConfig() @@ -65,15 +72,17 @@ public function verifyConfig() } /** - * @param $method - * @param $args + * Implementes magic methods for `newXXX` + * + * @param string $name Method name being called + * @param array $args arguments passed to the original method * * @return mixed * @throws \Exception */ - public function __call($method, $args) + public function __call($name, array $args) { - $type = Inflector::underscore(substr($method, 3)); + $type = Inflector::underscore(substr($name, 3)); if (!isset($args[0])) { $args[0] = []; @@ -85,9 +94,12 @@ public function __call($method, $args) } /** - * @param string|null $key + * Gets the real table + * + * @param string|null $key Table to find * * @return \Cake\ORM\Table + * * @throws \Exception */ protected function _table($key = null) @@ -111,10 +123,12 @@ protected function _table($key = null) } /** - * @param \Cake\Event\Event $event - * @param \Cake\ORM\Query $query - * @param \ArrayObject $options - * @param $primary + * @param \Cake\Event\Event $event Event + * @param \Cake\ORM\Query $query Quey + * @param \ArrayObject $options Options + * @param bool $primary If primary + * + * @return void */ public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) { @@ -138,9 +152,11 @@ public function beforeFind(Event $event, Query $query, ArrayObject $options, $pr } /** - * @param \Cake\Event\Event $event - * @param \Cake\Validation\Validator $validator - * @param $name + * @param \Cake\Event\Event $event Event + * @param \Cake\Validation\Validator $validator Original Validator + * @param string $name Name of validator that is being built + * + * @return void * * @throws \Exception */ @@ -169,9 +185,11 @@ public function buildValidator(Event $event, Validator $validator, $name) } /** - * @param \Cake\Event\Event $event - * @param \Cake\Datasource\EntityInterface $entity - * @param \ArrayObject $options + * @param \Cake\Event\Event $event Event + * @param \Cake\Datasource\EntityInterface $entity Entity to save + * @param \ArrayObject $options Options + * + * @return void * * @throws \Exception */ @@ -191,9 +209,11 @@ public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $o } /** - * @param \Cake\Event\Event $event - * @param \ArrayObject $data - * @param \ArrayObject $options + * @param \Cake\Event\Event $event Event + * @param \ArrayObject $data Data to marshall + * @param \ArrayObject $options Options + * + * @return void * * @throws \Exception */ @@ -212,8 +232,11 @@ public function beforeMarshal(Event $event, ArrayObject $data, ArrayObject $opti } /** - * @param $key - * @param $entityClass + * Adds the type and implements magic newXxx method + * @param string $key Key + * @param string $entityClass Entity to add + * + * @return void * * @throws \Exception */ diff --git a/src/Model/Entity/StiAwareTrait.php b/src/Model/Entity/StiAwareTrait.php index 573743d..2545738 100644 --- a/src/Model/Entity/StiAwareTrait.php +++ b/src/Model/Entity/StiAwareTrait.php @@ -3,6 +3,11 @@ trait StiAwareTrait { + /** + * Returns a copy of the entity + * + * @return array Properties for copying + */ public function forCopy() { return $this->_properties; From b679f0083a38737db2ddd09001df42866d31af26 Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:02:26 -0600 Subject: [PATCH 4/9] Fixing phpstan Signed-off-by: Chris Hallgren --- tests/phpstan.neon | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/phpstan.neon diff --git a/tests/phpstan.neon b/tests/phpstan.neon new file mode 100644 index 0000000..9139975 --- /dev/null +++ b/tests/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 5 + autoload_files: + - tests/bootstrap.php From 180828919535f8f7a0e884f555bc86c45096ff66 Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:17:45 -0600 Subject: [PATCH 5/9] Switching back to coveralls Signed-off-by: Chris Hallgren --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fa1fb8a..b47a37e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,10 +57,10 @@ script: - if [[ $CHECKS == 1 ]]; then composer phpstan-setup && composer phpstan; fi - if [[ $CHECKS == 1 ]]; then composer cs-check; fi - - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=clover.xml; fi + - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --stderr --coverage-clover build/logs/clover.xml; fi after_success: - - if [[ $CODECOVERAGE == 1 ]]; then bash <(curl -s https://codecov.io/bash); fi + - if [[ $CODECOVERAGE == 1 ]]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi" cache: directories: From fcffd126a8dc203b56374ac9bc640e44b90f681a Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:22:08 -0600 Subject: [PATCH 6/9] Fixing forgotten quote Signed-off-by: Chris Hallgren --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b47a37e..aadb10b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ script: - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --stderr --coverage-clover build/logs/clover.xml; fi after_success: - - if [[ $CODECOVERAGE == 1 ]]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi" + - if [[ $CODECOVERAGE == 1 ]]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi cache: directories: From c29f3792d68c789c1999e679a2f3b8ec2d84e00a Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:45:06 -0600 Subject: [PATCH 7/9] Updating coveralls Signed-off-by: Chris Hallgren --- .gitignore | 1 + .travis.yml | 2 +- composer.json | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0bcf9b9..d5fb51a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /vendor /tmp /phpcs.xml +/phpunit.phar diff --git a/.travis.yml b/.travis.yml index aadb10b..712409d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,7 +60,7 @@ script: - if [[ $CODECOVERAGE == 1 ]]; then phpdbg -qrr vendor/bin/phpunit --stderr --coverage-clover build/logs/clover.xml; fi after_success: - - if [[ $CODECOVERAGE == 1 ]]; then php vendor/bin/coveralls -c .coveralls.yml -v; fi + - if [[ $CODECOVERAGE == 1 ]]; then php vendor/bin/php-coveralls -c .coveralls.yml -v; fi cache: directories: diff --git a/composer.json b/composer.json index cc96fa9..5206eb6 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "cakephp/cakephp": "^3.8" }, "require-dev": { - "cakephp/cakephp-codesniffer": "^3.0" + "cakephp/cakephp-codesniffer": "^3.0", + "php-coveralls/php-coveralls": "^2.2" }, "autoload": { "psr-4": { From e688338d90def79ded9d234035d0b08d0a0eec02 Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:47:17 -0600 Subject: [PATCH 8/9] Removing php-coveralls package since travis already has it Signed-off-by: Chris Hallgren --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 5206eb6..cc96fa9 100644 --- a/composer.json +++ b/composer.json @@ -34,8 +34,7 @@ "cakephp/cakephp": "^3.8" }, "require-dev": { - "cakephp/cakephp-codesniffer": "^3.0", - "php-coveralls/php-coveralls": "^2.2" + "cakephp/cakephp-codesniffer": "^3.0" }, "autoload": { "psr-4": { From 9bad5594fa6001891904c2938edd1d6595bfc4a9 Mon Sep 17 00:00:00 2001 From: Chris Hallgren Date: Wed, 15 Jan 2020 15:53:10 -0600 Subject: [PATCH 9/9] We do need php-coveralls Signed-off-by: Chris Hallgren --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cc96fa9..5206eb6 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "cakephp/cakephp": "^3.8" }, "require-dev": { - "cakephp/cakephp-codesniffer": "^3.0" + "cakephp/cakephp-codesniffer": "^3.0", + "php-coveralls/php-coveralls": "^2.2" }, "autoload": { "psr-4": {