-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NEW: Static validation for relationships. (#9874)
* NEW: Static validation for relationships. * Unit tests added. * PR fixes * PR feedback: Execute validation on flush. * PR fixes. * PR fixes.
- 6.0.0-alpha1
- 5.3.5
- 5.3.4
- 5.3.3
- 5.3.2
- 5.3.1
- 5.3.0
- 5.3.0-rc1
- 5.3.0-beta1
- 5.2.22
- 5.2.21
- 5.2.20
- 5.2.19
- 5.2.18
- 5.2.17
- 5.2.16
- 5.2.15
- 5.2.14
- 5.2.13
- 5.2.12
- 5.2.11
- 5.2.10
- 5.2.9
- 5.2.8
- 5.2.7
- 5.2.6
- 5.2.5
- 5.2.4
- 5.2.3
- 5.2.2
- 5.2.1
- 5.2.0
- 5.2.0-rc1
- 5.2.0-beta1
- 5.1.23
- 5.1.22
- 5.1.21
- 5.1.20
- 5.1.19
- 5.1.18
- 5.1.17
- 5.1.16
- 5.1.15
- 5.1.14
- 5.1.13
- 5.1.12
- 5.1.11
- 5.1.10
- 5.1.9
- 5.1.8
- 5.1.7
- 5.1.6
- 5.1.5
- 5.1.4
- 5.1.3
- 5.1.2
- 5.1.1
- 5.1.0
- 5.1.0-rc1
- 5.1.0-beta1
- 5.0.23
- 5.0.22
- 5.0.21
- 5.0.20
- 5.0.19
- 5.0.18
- 5.0.17
- 5.0.16
- 5.0.15
- 5.0.14
- 5.0.13
- 5.0.12
- 5.0.11
- 5.0.10
- 5.0.9
- 5.0.8
- 5.0.7
- 5.0.6
- 5.0.5
- 5.0.4
- 5.0.3
- 5.0.2
- 5.0.1
- 5.0.0
- 5.0.0-rc1
- 5.0.0-beta3
- 5.0.0-beta2
- 5.0.0-beta1
- 5.0.0-alpha1
- 4.13.44
- 4.13.43
- 4.13.42
- 4.13.41
- 4.13.40
- 4.13.39
- 4.13.38
- 4.13.37
- 4.13.36
- 4.13.35
- 4.13.34
- 4.13.33
- 4.13.32
- 4.13.31
- 4.13.30
- 4.13.29
- 4.13.28
- 4.13.27
- 4.13.26
- 4.13.25
- 4.13.24
- 4.13.23
- 4.13.22
- 4.13.21
- 4.13.20
- 4.13.19
- 4.13.18
- 4.13.17
- 4.13.16
- 4.13.15
- 4.13.14
- 4.13.13
- 4.13.12
- 4.13.11
- 4.13.10
- 4.13.9
- 4.13.8
- 4.13.7
- 4.13.6
- 4.13.5
- 4.13.4
- 4.13.3
- 4.13.2
- 4.13.1
- 4.13.0
- 4.13.0-rc1
- 4.13.0-beta1
- 4.12.7
- 4.12.6
- 4.12.5
- 4.12.4
- 4.12.3
- 4.12.2
- 4.12.1
- 4.12.0
- 4.12.0-rc1
- 4.12.0-beta1
- 4.11.16
- 4.11.15
- 4.11.14
- 4.11.13
- 4.11.12
- 4.11.11
- 4.11.10
- 4.11.9
- 4.11.8
- 4.11.7
- 4.11.6
- 4.11.5
- 4.11.4
- 4.11.3
- 4.11.2
- 4.11.1
- 4.11.0
- 4.11.0-rc1
- 4.11.0-beta3
- 4.11.0-beta2
- 4.11.0-beta1
1 parent
8f1c68d
commit 89c87dd
Showing
10 changed files
with
1,168 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Validation; | ||
|
||
use ReflectionException; | ||
use SilverStripe\Core\Extension; | ||
use SilverStripe\ORM\DatabaseAdmin; | ||
|
||
/** | ||
* Hook up static validation to the deb/build process | ||
* | ||
* @method DatabaseAdmin getOwner() | ||
*/ | ||
class DatabaseAdminExtension extends Extension | ||
{ | ||
/** | ||
* Extension point in @see DatabaseAdmin::doBuild() | ||
* | ||
* @param bool $quiet | ||
* @param bool $populate | ||
* @param bool $testMode | ||
* @throws ReflectionException | ||
*/ | ||
public function onAfterBuild(bool $quiet, bool $populate, bool $testMode): void | ||
{ | ||
$service = RelationValidationService::singleton(); | ||
|
||
if (!$service->config()->get('output_enabled')) { | ||
return; | ||
} | ||
|
||
$service->executeValidation(); | ||
} | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Tests\Validation; | ||
|
||
use SilverStripe\Dev\TestOnly; | ||
use SilverStripe\ORM\DataObject; | ||
|
||
/** | ||
* Class Freelancer | ||
* | ||
* @property string $Title | ||
* @method Team TemporaryTeam() | ||
* @method Member TemporaryMember() | ||
*/ | ||
class Freelancer extends DataObject implements TestOnly | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private static $table_name = 'RelationValidationTest_Freelancer'; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $db = [ | ||
'Title' => 'Varchar(255)', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $has_one = [ | ||
'TemporaryTeam' => Team::class, | ||
'TemporaryMember' => Member::class, | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Tests\Validation; | ||
|
||
use SilverStripe\Dev\TestOnly; | ||
use SilverStripe\ORM\DataObject; | ||
use SilverStripe\ORM\ManyManyList; | ||
|
||
/** | ||
* Class Hat | ||
* | ||
* @property string $Title | ||
* @method Member Hatter() | ||
* @method ManyManyList|Team[] TeamHats() | ||
*/ | ||
class Hat extends DataObject implements TestOnly | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private static $table_name = 'RelationValidationTest_Hat'; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $db = [ | ||
'Title' => 'Varchar(255)', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $belongs_to = [ | ||
'Hatter' => Member::class . '.Hat', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $belongs_many_many = [ | ||
'TeamHats' => Team::class . '.ReserveHats', | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Tests\Validation; | ||
|
||
use SilverStripe\Dev\TestOnly; | ||
use SilverStripe\ORM\DataObject; | ||
use SilverStripe\ORM\HasManyList; | ||
use SilverStripe\ORM\ManyManyThroughList; | ||
|
||
/** | ||
* Class Member | ||
* | ||
* @property string $Title | ||
* @method Team HomeTeam() | ||
* @method Hat Hat() | ||
* @method HasManyList|Freelancer[] TemporaryMembers() | ||
* @method ManyManyThroughList|Member[] FreelancerTeams() | ||
*/ | ||
class Member extends DataObject implements TestOnly | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private static $table_name = 'RelationValidationTest_Member'; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $db = [ | ||
'Title' => 'Varchar(255)', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $has_one = [ | ||
'HomeTeam' => Team::class, | ||
'Hat' => Hat::class, | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $has_many = [ | ||
'TemporaryMembers' => Freelancer::class . '.TemporaryMember', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $belongs_many_many = [ | ||
'FreelancerTeams' => Team::class . '.Freelancers', | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,291 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Tests\Validation; | ||
|
||
use Page; | ||
use SilverStripe\Core\Config\Config; | ||
use SilverStripe\Dev\SapphireTest; | ||
use SilverStripe\Dev\Validation\RelationValidationService; | ||
|
||
class RelationValidationTest extends SapphireTest | ||
{ | ||
/** | ||
* @var array | ||
*/ | ||
protected static $extra_dataobjects = [ | ||
Team::class, | ||
Member::class, | ||
Hat::class, | ||
Freelancer::class, | ||
]; | ||
|
||
/** | ||
* @param string|null $class | ||
* @param string|null $field | ||
* @param array $value | ||
* @param array $expected | ||
* @dataProvider validateCasesProvider | ||
*/ | ||
public function testValidation(?string $class, ?string $field, array $value, array $expected): void | ||
{ | ||
if ($class && $field) { | ||
Config::modify()->set($class, $field, $value); | ||
} | ||
|
||
$data = RelationValidationService::singleton()->inspectClasses([ | ||
Team::class, | ||
Member::class, | ||
Hat::class, | ||
Freelancer::class, | ||
]); | ||
|
||
$this->assertSame($expected, $data); | ||
} | ||
|
||
/** | ||
* @param string $class | ||
* @param string|null $relation | ||
* @param array $config | ||
* @param bool $expected | ||
* @dataProvider ignoredClassesProvider | ||
*/ | ||
public function testIgnoredClass(string $class, ?string $relation, array $config, bool $expected): void | ||
{ | ||
$service = RelationValidationService::singleton(); | ||
|
||
foreach ($config as $name => $value) { | ||
$service->config()->set($name, $value); | ||
} | ||
|
||
$result = $service->isIgnored($class, $relation); | ||
|
||
$this->assertEquals($expected, $result); | ||
} | ||
|
||
public function validateCasesProvider(): array | ||
{ | ||
return [ | ||
'correct setup' => [ | ||
null, | ||
null, | ||
[], | ||
[], | ||
], | ||
'ambiguous has_one - no relation name' => [ | ||
Hat::class, | ||
'belongs_to', | ||
[ | ||
'Hatter' => Member::class, | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Member / Hat : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Hat / Hatter : Relation is not in the expected format (needs class.relation format)', | ||
], | ||
], | ||
'ambiguous has_one - incorrect relation name' => [ | ||
Hat::class, | ||
'belongs_to', | ||
[ | ||
'Hatter' => Member::class . '.ObviouslyWrong', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Member / Hat : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Hat / Hatter : Back relation not found', | ||
], | ||
], | ||
'ambiguous has_one - too many relations' => [ | ||
Hat::class, | ||
'belongs_to', | ||
[ | ||
'Hatter' => Member::class . '.Hat', | ||
'HatterCopy' => Member::class . '.Hat', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Member / Hat : Back relation is ambiguous', | ||
], | ||
], | ||
'invalid has one' => [ | ||
Member::class, | ||
'has_one', | ||
[ | ||
'HomeTeam' => Team::class . '.UnnecessaryRelation', | ||
'Hat' => Hat::class, | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Member / HomeTeam : Relation SilverStripe\Dev\Tests\Validation\Team.UnnecessaryRelation is not in the expected format (needs class only format).' | ||
], | ||
], | ||
'ambiguous has_many - no relation name' => [ | ||
Team::class, | ||
'has_many', | ||
[ | ||
'Members' => Member::class, | ||
'FreelancerMembers' => Freelancer::class . '.TemporaryTeam', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / Members : Relation is not in the expected format (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Member / HomeTeam : Back relation not found or ambiguous (needs class.relation format)', | ||
], | ||
], | ||
'ambiguous has_many - incorrect relation name' => [ | ||
Team::class, | ||
'has_many', | ||
[ | ||
'Members' => Member::class . '.ObviouslyWrong', | ||
'FreelancerMembers' => Freelancer::class . '.TemporaryTeam', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / Members : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Member / HomeTeam : Back relation not found or ambiguous (needs class.relation format)', | ||
], | ||
], | ||
'ambiguous has_many - too many relations' => [ | ||
Team::class, | ||
'has_many', | ||
[ | ||
'Members' => Member::class . '.HomeTeam', | ||
'MembersCopy' => Member::class . '.HomeTeam', | ||
'FreelancerMembers' => Freelancer::class . '.TemporaryTeam', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Member / HomeTeam : Back relation is ambiguous', | ||
], | ||
], | ||
'ambiguous many_many - no relation name' => [ | ||
Hat::class, | ||
'belongs_many_many', | ||
[ | ||
'TeamHats' => Team::class, | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / ReserveHats : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Hat / TeamHats : Relation is not in the expected format (needs class.relation format)', | ||
], | ||
], | ||
'ambiguous many_many - incorrect relation name' => [ | ||
Hat::class, | ||
'belongs_many_many', | ||
[ | ||
'TeamHats' => Team::class . '.ObviouslyWrong', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / ReserveHats : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Hat / TeamHats : Back relation not found', | ||
], | ||
], | ||
'ambiguous many_many - too many relations' => [ | ||
Hat::class, | ||
'belongs_many_many', | ||
[ | ||
'TeamHats' => Team::class . '.ReserveHats', | ||
'TeamHatsCopy' => Team::class . '.ReserveHats', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / ReserveHats : Back relation is ambiguous', | ||
], | ||
], | ||
'ambiguous many_many through - no relation name' => [ | ||
Member::class, | ||
'belongs_many_many', | ||
[ | ||
'FreelancerTeams' => Team::class, | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / Freelancers : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Member / FreelancerTeams : Relation is not in the expected format (needs class.relation format)', | ||
], | ||
], | ||
'ambiguous many_many through - incorrect relation name' => [ | ||
Member::class, | ||
'belongs_many_many', | ||
[ | ||
'FreelancerTeams' => Team::class . '.ObviouslyWrong', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / Freelancers : Back relation not found or ambiguous (needs class.relation format)', | ||
'SilverStripe\Dev\Tests\Validation\Member / FreelancerTeams : Back relation not found', | ||
], | ||
], | ||
'ambiguous many_many through - too many relations' => [ | ||
Member::class, | ||
'belongs_many_many', | ||
[ | ||
'FreelancerTeams' => Team::class . '.Freelancers', | ||
'FreelancerTeamsCopy' => Team::class . '.Freelancers', | ||
], | ||
[ | ||
'SilverStripe\Dev\Tests\Validation\Team / Freelancers : Back relation is ambiguous', | ||
], | ||
], | ||
]; | ||
} | ||
|
||
public function ignoredClassesProvider(): array | ||
{ | ||
return [ | ||
'class default' => [ | ||
Team::class, | ||
null, | ||
[], | ||
true, | ||
], | ||
'class relation default' => [ | ||
Team::class, | ||
'Members', | ||
[], | ||
true, | ||
], | ||
'page should by included by default (empty namespace)' => [ | ||
Page::class, | ||
null, | ||
[], | ||
false, | ||
], | ||
'class relation via allow rules' => [ | ||
Team::class, | ||
'Members', | ||
[ | ||
'allow_rules' => ['app' => 'SilverStripe\Dev\Tests\Validation'], | ||
], | ||
false, | ||
], | ||
'class included via allow rules but overwritten by deny rules' => [ | ||
Team::class, | ||
null, | ||
[ | ||
'allow_rules' => ['app' => 'SilverStripe\Dev\Tests\Validation'], | ||
'deny_rules' => [Team::class], | ||
], | ||
true, | ||
], | ||
'class relation included via allow rules but overwritten by deny rules' => [ | ||
Team::class, | ||
'Members', | ||
[ | ||
'allow_rules' => ['app' => 'SilverStripe\Dev\Tests\Validation'], | ||
'deny_rules' => [Team::class], | ||
], | ||
true, | ||
], | ||
'class relation included via allow rules but overwritten by deny relations' => [ | ||
Team::class, | ||
'Members', | ||
[ | ||
'allow_rules' => ['app' => 'SilverStripe\Dev\Tests\Validation'], | ||
'deny_relations' => [Team::class . '.Members'], | ||
], | ||
true, | ||
], | ||
'class relation included via allow rules and not overwritten by deny relations of other class' => [ | ||
Member::class, | ||
'HomeTeam', | ||
[ | ||
'allow_rules' => ['app' => 'SilverStripe\Dev\Tests\Validation'], | ||
'deny_rules' => [Team::class], | ||
'deny_relations' => [Team::class . '.Members'], | ||
], | ||
false, | ||
], | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
namespace SilverStripe\Dev\Tests\Validation; | ||
|
||
use SilverStripe\Dev\TestOnly; | ||
use SilverStripe\ORM\DataObject; | ||
use SilverStripe\ORM\HasManyList; | ||
use SilverStripe\ORM\ManyManyList; | ||
use SilverStripe\ORM\ManyManyThroughList; | ||
|
||
/** | ||
* Class Team | ||
* | ||
* @property string $Title | ||
* @method HasManyList|Member[] Members() | ||
* @method HasManyList|Freelancer[] FreelancerMembers() | ||
* @method ManyManyThroughList|Member[] Freelancers() | ||
* @method ManyManyList|Hat[] ReserveHats() | ||
*/ | ||
class Team extends DataObject implements TestOnly | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
private static $table_name = 'RelationValidationTest_Team'; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $db = [ | ||
'Title' => 'Varchar(255)', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $has_many = [ | ||
'Members' => Member::class . '.HomeTeam', | ||
'FreelancerMembers' => Freelancer::class . '.TemporaryTeam', | ||
]; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
private static $many_many = [ | ||
'ReserveHats' => Hat::class, | ||
'Freelancers' => [ | ||
'through' => Freelancer::class, | ||
'from' => 'TemporaryTeam', | ||
'to' => 'TemporaryMember', | ||
], | ||
]; | ||
} |