Skip to content

Commit 47c19ef

Browse files
authored
Add support to exclude specific relationships. Update docs (#14)
* Add support to exclude specific relationships. Update docs * Update var name
1 parent 9b0fdd3 commit 47c19ef

3 files changed

Lines changed: 148 additions & 32 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ Generate a YAML fixture from DataObjects
77
- [Warnings](#warnings)
88
- [Dev task](#dev-task)
99
- [General usage](#general-usage)
10+
- [Set a maximum depth to export](#set-a-maximum-depth-to-export)
11+
- [Excluding relationships from export](#excluding-relationships-from-export)
1012
- [Excluding classes from export](#excluding-classes-from-export)
1113
- [Common errors](#common-errors)
1214
- [Supported relationships](#supported-relationships)
@@ -86,6 +88,17 @@ $fixture = Director::baseFolder() . '/app/resources/fixture.yml';
8688
file_put_contents($fixture, $service->outputFixture());
8789
```
8890

91+
## Set a maximum depth to export
92+
93+
If you are having issues with "nesting level of '256' reached", then one option is to set a maximum depth that the
94+
Service will attempt to export.
95+
96+
```php
97+
// Instantiate the Service.
98+
$service = new FixtureService();
99+
$service->setAllowedDepth(2);
100+
```
101+
89102
## Excluding classes from export
90103

91104
There might be some classes (like Members?) that you don't want to include in your fixture. The manifest will check
@@ -117,8 +130,46 @@ SilverStripe\Security\Member:
117130
exclude_from_fixture_relationships: 0
118131
```
119132

133+
## Excluding relationships from export
134+
135+
Similar to excluding classes, there might be some specific relationships on specific classes that you want to exclude.
136+
Perhaps you have identified a looping relationship, and you would like to exclude one of them to make things
137+
predictable, or perhaps it's just a relationship you don't need in your fixtures.
138+
139+
You can exclude specific relationships by adding `excluded_fixture_relationships` to the desired class.
140+
141+
`excluded_fixture_relationships` accepts an array of **relationship names**.
142+
143+
EG:
144+
```php
145+
class MyModel extends DataObject
146+
{
147+
private static $has_one = [
148+
'FeatureImage' => Image::class,
149+
];
150+
}
151+
```
152+
153+
```yaml
154+
App\Models\MyModel:
155+
excluded_fixture_relationships:
156+
- FeatureImage
157+
```
158+
120159
## Common errors
121160

161+
### Nesting level of '256' reached
162+
163+
Above are three options that you can use to attempt to reduce this.
164+
165+
- [Set a maximum depth to export](#set-a-maximum-depth-to-export)
166+
- [Excluding relationships from export](#excluding-relationships-from-export)
167+
- [Excluding classes from export](#excluding-classes-from-export)
168+
169+
I would recommend that you begin by exluding classes that you don't need for your export, then move to excluding
170+
specific relationships that might be causing deep levels of nested relationships, and finally, if those fail, you can
171+
set a max depth.
172+
122173
### DataObject::get() cannot query non-subclass DataObject directly
123174

124175
You might see this error if you have a relationship being defined as simply `DataObject::class`, EG:

src/Manifest/RelationshipManifest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use ChrisPenny\DataObjectToFixture\Helper\KahnSorter;
66
use ChrisPenny\DataObjectToFixture\ORM\Group;
77
use Exception;
8+
use SilverStripe\Core\Config\Config;
89

910
class RelationshipManifest
1011
{
@@ -14,6 +15,11 @@ class RelationshipManifest
1415
*/
1516
private $relationships = [];
1617

18+
/**
19+
* @var array
20+
*/
21+
private $excludedRelationships = [];
22+
1723
/**
1824
* @return array
1925
*/
@@ -27,11 +33,24 @@ public function getRelationships(): array
2733
*/
2834
public function addGroup(Group $group): void
2935
{
36+
// If we've added this group to relationships already, then we have everything we need
3037
if (array_key_exists($group->getClassName(), $this->relationships)) {
3138
return;
3239
}
3340

41+
// Add this group as a new relationship
3442
$this->relationships[$group->getClassName()] = [];
43+
44+
// Check to see if this class has any relationships that it wants to exclude as we process it
45+
$excludes = Config::inst()->get($group->getClassName(), 'excluded_fixture_relationships');
46+
47+
// No it doesn't, so we can just return here
48+
if (!$excludes) {
49+
return;
50+
}
51+
52+
// Add the list of relationships to exclude from our fixtures
53+
$this->excludedRelationships[$group->getClassName()] = $excludes;
3554
}
3655

3756
/**
@@ -75,11 +94,38 @@ public function removeRelationship(string $fromClass, string $toClass): void
7594
unset($this->relationships[$toClass][$key]);
7695
}
7796

97+
/**
98+
* @param string $className
99+
* @param string $relationshipName
100+
* @return bool
101+
*/
102+
public function shouldExcludeRelationship(string $className, string $relationshipName): bool
103+
{
104+
if (!array_key_exists($className, $this->excludedRelationships)) {
105+
return false;
106+
}
107+
108+
$exclusions = $this->excludedRelationships[$className];
109+
110+
return in_array($relationshipName, $exclusions);
111+
}
112+
113+
/**
114+
* @return array
115+
*/
78116
public function getPrioritisedOrder(): array
79117
{
80118
$kahnSorter = new KahnSorter($this->getRelationships());
81119

82120
return $kahnSorter->sort();
83121
}
84122

123+
/**
124+
* @return array
125+
*/
126+
public function getExcludedRelationships(): array
127+
{
128+
return $this->excludedRelationships;
129+
}
130+
85131
}

src/Service/FixtureService.php

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,31 @@ public function getWarnings(): array
174174
return $this->warnings;
175175
}
176176

177+
/**
178+
* @return int
179+
*/
180+
public function getAllowedDepth(): ?int
181+
{
182+
return $this->allowedDepth;
183+
}
184+
185+
/**
186+
* @param int $allowedDepth
187+
* @return FixtureService
188+
*/
189+
public function setAllowedDepth(int $allowedDepth = null): FixtureService
190+
{
191+
if ($allowedDepth === 0) {
192+
$this->addWarning('You set an allowed depth of 0. We have assumed you meant 1.');
193+
194+
$allowedDepth = 1;
195+
}
196+
197+
$this->allowedDepth = $allowedDepth;
198+
199+
return $this;
200+
}
201+
177202
/**
178203
* @return array
179204
*/
@@ -271,9 +296,18 @@ protected function addDataObjectHasOneFields(DataObject $dataObject, int $curren
271296
}
272297

273298
// This class has requested that it not be included in relationship maps.
274-
$exclude = Config::inst()->get($relationFieldName, 'exclude_from_fixture_relationships');
299+
$excludeClass = Config::inst()->get($relationClassName, 'exclude_from_fixture_relationships');
275300

276-
if ($exclude) {
301+
if ($excludeClass) {
302+
continue;
303+
}
304+
305+
$excludeRelationship = $this->relationshipManifest->shouldExcludeRelationship(
306+
$dataObject->ClassName,
307+
$fieldName
308+
);
309+
310+
if ($excludeRelationship) {
277311
continue;
278312
}
279313

@@ -385,9 +419,18 @@ protected function addDataObjectHasManyFields(DataObject $dataObject, int $curre
385419
$schema->getRemoteJoinField($dataObject->ClassName, $relationFieldName, 'has_many');
386420

387421
// This class has requested that it not be included in relationship maps.
388-
$exclude = Config::inst()->get($cleanRelationshipClassName, 'exclude_from_fixture_relationships');
422+
$excludeClass = Config::inst()->get($cleanRelationshipClassName, 'exclude_from_fixture_relationships');
389423

390-
if ($exclude) {
424+
if ($excludeClass) {
425+
continue;
426+
}
427+
428+
$excludeRelationship = $this->relationshipManifest->shouldExcludeRelationship(
429+
$dataObject->ClassName,
430+
$relationFieldName
431+
);
432+
433+
if ($excludeRelationship) {
391434
continue;
392435
}
393436

@@ -662,9 +705,10 @@ protected function removeLoopingRelationships(array $parentage, array $toClasses
662705
$this->relationshipManifest->removeRelationship($toClass, $loopingRelationship);
663706

664707
$this->addWarning(sprintf(
665-
'A relationships was removed between "%s" and "%s". This occurs if we have detected a loop'
666-
. '. Until belongs_to relationships are supported in fixtures, you might not be able to rely on'
667-
. ' fixtures generated to have the appropriate priority order',
708+
'A relationships was removed between "%s" and "%s". This occurs if we have detected a'
709+
. ' loop . Until belongs_to relationships are supported in fixtures, you might not be able'
710+
. ' to rely on fixtures generated to have the appropriate priority order. You might want to'
711+
. ' consider adding one of these relationships to `excluded_fixture_relationships`.',
668712
$loopingRelationship,
669713
$toClass
670714
));
@@ -711,29 +755,4 @@ protected function addWarning(string $message): void
711755
$this->warnings[] = $message;
712756
}
713757

714-
/**
715-
* @return int
716-
*/
717-
public function getAllowedDepth(): ?int
718-
{
719-
return $this->allowedDepth;
720-
}
721-
722-
/**
723-
* @param int $allowedDepth
724-
* @return FixtureService
725-
*/
726-
public function setAllowedDepth(int $allowedDepth = null): FixtureService
727-
{
728-
if ($allowedDepth === 0) {
729-
$this->addWarning('You set an allowed depth of 0. We have assumed you meant 1.');
730-
731-
$allowedDepth = 1;
732-
}
733-
734-
$this->allowedDepth = $allowedDepth;
735-
736-
return $this;
737-
}
738-
739758
}

0 commit comments

Comments
 (0)