Skip to content

Commit 62e6949

Browse files
authored
Add support for many_many and looping relationships. Remove Fluent support (#21)
1 parent e7b2be5 commit 62e6949

10 files changed

Lines changed: 752 additions & 602 deletions

File tree

README.md

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ Generate a YAML fixture from DataObjects
44
- [Installation](#installation)
55
- [Purpose (early stage)](#purpose-(early-stage))
66
- [Purpose (future development)](#purpose-(future-development))
7-
- [Warnings](#warnings)
8-
- [Dev task](#dev-task)
97
- [General usage](#general-usage)
10-
- [Set a maximum depth to export](#set-a-maximum-depth-to-export)
8+
- [Warnings](#warnings)
9+
- [Dev task](#dev-task)
1110
- [Excluding relationships from export](#excluding-relationships-from-export)
1211
- [Excluding classes from export](#excluding-classes-from-export)
1312
- [Common issues](#common-issues)
13+
- [Parent Pages included in your export](#parent-pages-included-in-your-export)
14+
- [Node `[YourClass]` has `[x]` left over dependencies, and so could not be sorted](#node-yourclass-has-x-left-over-dependencies-and-so-could-not-be-sorted)
15+
- [Request timeout when generating fixtures](#request-timeout-when-generating-fixtures)
16+
- [DataObject::get() cannot query non-subclass DataObject directly](#dataobjectget-cannot-query-non-subclass-dataobject-directly)
1417
- [Supported relationships](#supported-relationships)
15-
- [Unsupported relationships](#unsupported-relationships)
1618
- [Fluent support](#fluent-support)
1719
- [Future features](#future-features)
1820
- [Things that this module does not currently do](#things-that-this-module-does-not-currently-do)
@@ -25,24 +27,33 @@ composer require chrispenny/silverstripe-data-object-to-fixture
2527

2628
## Purpose (early stage)
2729

28-
The purpose of this module (at this early stage) is not to create perfect fixtures, but more to provide a solid
29-
guideline for what your fixture should look like.
30+
The purpose of this module (at this early stage) is not to guarantee perfect fixtures every time, but more to provide a
31+
solid guideline for what your fixture should look like.
3032

3133
For example:
3234
Writing unit test fixtures can be difficult, especially when you're needing to visualise the structure and relationships
3335
of many different DataObjects (and then add an extra layer if you're using, say, Fluent).
3436

37+
I would also like this module to work well with the
38+
[Populate](https://github.com/silverstripe/silverstripe-populate) module. Please note though that you'll need to be
39+
running version [2.1 or greater](https://github.com/silverstripe/silverstripe-populate/releases/tag/2.1.0), as versions
40+
before that did not support circular relationships.
41+
3542
## Purpose (future development)
3643

37-
One goal for the future of this module is for it to work in tandem with the Populate module. I would like to get to a
38-
stage where content authors are able to (for example) "export pages" through the CMS, so that those pages can then be
39-
recreated via Populate (without a dev needing to create the fixture themselves).
44+
My dream for this module is that I would like to get to a stage where we can confidently say that generated fixtures
45+
will be perfect every time.
46+
47+
From there, I could see this being used (as an example) for testers to be able to export pages through the CMS on their
48+
test environments, so that those pages can then be restored at any time via (maybe) Populate. How this would work
49+
exactly, and whether or not it would use Populate, is still to be determined.
4050

4151
## Warnings
4252

43-
This is in very, very early development stages. Please be aware that:
53+
This is still in early development stages. Please be aware that:
4454

4555
- Classes might change
56+
- Return types might change
4657
- Entire paradigms on how I generate the fixtures might change
4758

4859
What won't change:
@@ -53,14 +64,16 @@ I would not recommend that you use this module (at this stage) for any applicati
5364
recommend that you use it as a developer tool (EG: to help you write your own fixtures, either for tests, or to be used
5465
with Populate).
5566

56-
## Dev task
67+
## General usage
68+
69+
### Dev task
5770

5871
A dev task can be found at `/dev/tasks/generate-fixture-from-dataobject`.
5972

6073
This task will allow you to generate a fixture (output on the screen for you to copy/paste) for any DataObject that
6174
you have defined in your project.
6275

63-
## General usage
76+
### Code use
6477

6578
```php
6679
// Instantiate the Service.
@@ -73,31 +86,23 @@ $page = Page::get()->byID(1);
7386
// Add the DataObject to the Service.
7487
$service->addDataObject($dataObject);
7588

89+
// Generating the fixture can also generate new warnings
90+
$output = $service->outputFixture();
91+
7692
// Check for warnings? This is somewhat important, because if you have looping relationships (which we have no way of
7793
// creating fixtures for at the moment) this is how you'll know about it.
7894
if (count($service->getWarnings()) > 0) {
7995
Debug::dump($service->getWarnings());
8096
}
8197

8298
// Do something with the fixture output.
83-
highlight_string($service->outputFixture());
99+
highlight_string($output);
84100

85101
// Or maybe save the output to a file?
86102
$fixture = Director::baseFolder() . '/app/resources/fixture.yml';
87103
file_put_contents($fixture, $service->outputFixture());
88104
```
89105

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

103108
There might be some classes (like Members?) that you don't want to include in your fixture. The manifest will check
@@ -157,34 +162,55 @@ App\Models\MyModel:
157162

158163
## Common issues
159164

160-
### Relationship to ElementalArea missing
165+
### Parent Pages included in your export
161166

162-
TL;DR: There is a looping relationship between `Page` and `ElementalArea` in later versions of Elemental. You can
163-
exclude the `has_one` on `ElementalArea` as follows.
167+
When you're exporting Pages, if that Page has a `Parent`, then that `Parent` is considered a valid relationship, and
168+
so it will get exported along with the Page you've selected.
164169

170+
I'm still considering what to do about this, but for now, I would probably recommend that you add `Parent` to the list
171+
of excluded relationships for `SiteTree`:
172+
173+
```yaml
174+
SilverStripe\CMS\Model\SiteTree:
175+
excluded_fixture_relationships:
176+
- Parent
165177
```
166-
DNADesign\Elemental\Models\ElementalArea:
178+
179+
### Node `[YourClass]` has `[x]` left over dependencies, and so could not be sorted
180+
181+
This generally happens when you have a looping relationship. EG: `Page` `has_one` `Link`, and `Link` `has_one` back to
182+
`Page`. The sorter cannot determine which class should be prioritised above the other.
183+
184+
This doesn't necessarily mean that things will break, but it's worth reviewing. You might find that you can exclude one
185+
of the relationships in order to make thing more consistent.
186+
187+
A good example of this is in Elemental. Elemental provides an extension called `TopPage` which provides a relationship
188+
directly from each `BaseElement` to the `Page` that it belongs to (it's like a "index" so that you can loop up your
189+
`Page` from the `BaseElement` with less DB queries). This is handy for developers, but less handy for YAML fixtures.
190+
We'd actually prefer to exclude this relationship and follow the correct relationship flow from `Page` to
191+
`ElementalArea` to `BaseElement`.
192+
193+
I could exclude this relationship by adding the following configuration:
194+
```yaml
195+
DNADesign\Elemental\Models\BaseElement:
167196
excluded_fixture_relationships:
168197
- TopPage
169198
```
170199

171-
Extra info: [Issue](https://github.com/chrispenny/silverstripe-data-object-to-fixture/issues/13).
172-
173-
### Nesting level of '256' reached
200+
### Request timeout when generating fixtures
174201

175-
Above are three options that you can use to attempt to reduce this.
202+
Above are two options that you can use to attempt to reduce this.
176203

177-
- [Set a maximum depth to export](#set-a-maximum-depth-to-export)
178204
- [Excluding relationships from export](#excluding-relationships-from-export)
179205
- [Excluding classes from export](#excluding-classes-from-export)
180206

181207
I would recommend that you begin by exluding classes that you don't need for your export, then move to excluding
182-
specific relationships that might be causing deep levels of nested relationships, and finally, if those fail, you can
183-
set a max depth.
208+
specific relationships that might be causing deep levels of nested relationships.
184209

185210
### DataObject::get() cannot query non-subclass DataObject directly
186211

187-
You might see this error if you have a relationship being defined as simply `DataObject::class`, EG:
212+
You might see this error if you have polymorphic relationships (relationships being defined as simply
213+
`DataObject::class`), EG:
188214

189215
```php
190216
private static $has_one = [
@@ -216,21 +242,12 @@ method.
216242

217243
- `has_one`
218244
- `has_many`
219-
- `many_many` where a `through` relationship has been defined.
220-
221-
## Unsupported relationships
222-
223-
- `many_many` where **no** `through` relationship has been defined (you should be using `through`.... Use `through`).
224-
- `has_one` relationships that result in a loop of relationships (`belongs_to` is the "backward" definition for a
225-
`has_one` relationship, unfortunately, this is not currently supported in fixtures, so, we have no way to create a
226-
fixture for relationships that loop).
245+
- `many_many` (with and without `through` definitions)
227246

228247
## Fluent support
229248

230-
It is my intention to support Fluent and exporting Localised fields.
231-
232-
Current state *does* support this, but, again, this is still very early development, so you should be validating
233-
everything before you just go right ahead and assume it's perfect.
249+
It is my intention to support Fluent and exporting Localised fields in the future, but at this time, there is no
250+
support provided.
234251

235252
## Future features
236253

@@ -240,7 +257,5 @@ everything before you just go right ahead and assume it's perfect.
240257
## Things that this module does not currently do
241258

242259
- Export `_Live` tables. I hope to add `_Live` table exports soon(ish).
243-
- There is no ordering logic for records within the same class. This means that if you have classes that can have
244-
relationships to itself, the order or records might not be correct.
245260
- Support for exporting/saving away Asset binary files has not been added. This means that in the current state, you can
246261
only generate the database record for an Asset.

composer.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
},
2424
"require-dev": {
2525
"phpunit/phpunit": "^9.5",
26-
"tractorcow/silverstripe-fluent": ">=4.2.0",
27-
"slevomat/coding-standard": "~6.0"
26+
"slevomat/coding-standard": "^8"
2827
},
2928
"autoload": {
3029
"psr-4": {
@@ -33,5 +32,13 @@
3332
}
3433
},
3534
"prefer-stable": true,
36-
"minimum-stability": "dev"
35+
"minimum-stability": "dev",
36+
"config": {
37+
"allow-plugins": {
38+
"composer/installers": true,
39+
"silverstripe/vendor-plugin": true,
40+
"silverstripe/recipe-plugin": true,
41+
"dealerdirect/phpcodesniffer-composer-installer": true
42+
}
43+
}
3744
}

0 commit comments

Comments
 (0)