Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fluent and clean up #32

Merged
merged 6 commits into from
Oct 20, 2021

Conversation

adrhumphreys
Copy link
Contributor

@adrhumphreys adrhumphreys commented Aug 7, 2020

This allows you to now support using the module with fluent
It also splits out objects into classes which we can find the tables for and then tables which are just truncated
Task is now run from populate-task and documented correctly for running it

Copy link
Contributor

@jakxnz jakxnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. I was actually just picking this branch up for a project and thought I'd give it a quick review.

Also, no PR emoji @adrhumphreys ? 😎

}

/*
* Attempts to truncate a table if it hasn't already been truncated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth completing the docBlock?

}
}

if ($obj->hasExtension(FluentExtension::class)) {
Copy link
Contributor

@jakxnz jakxnz Sep 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of baking this directly into the logic, could you create an extension point here, and use the config to apply an extension for fluent, when fluent is in the project?

i.e

Only:
  moduleexists: 'Fluent'

While it's a complete breakdown if fluent is installed, it still feels quite opinionated to include this concern here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea better in general, but we already have hooks in here for checking whether the object is Versioned or not (Versioned is part of core but it's still optional on objects).

Let's merge this in as-is, and then I'll create an issue to suggest this change.

@adrhumphreys
Copy link
Contributor Author

@jakxnz I've become old and can't relate to emojis anymore 😂

You might want to try out https://github.com/adrhumphreys/silverstripe-fixtures

I found that with Fluent, Elemental, and Populate, you end up spending more time debugging missing data in some key table or some such. I still need to get around to updating this PR to reflect the changes I made when making the module

README.md Outdated
Example:
```yaml
DNADesign\Populate\Populate:
truncate_classes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding an "upgrade" note, for users who don't notice the change in terminology here?

private static function deleteClass(string $class): void
{
// First delete the base classes
$tableClasses = ClassInfo::ancestry($class, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to also capture downstream tables as well, e.g ClassInfo::dataClassesFor(), so only capturing ancestry is quite a deviation. What's the advantage of the change?

@jakxnz
Copy link
Contributor

jakxnz commented Sep 7, 2020

I did end up testing this branch, and found that it didn't quite manage a clean slate, and so my populate task crashed due to duplicate keys.

You might want to try out https://github.com/adrhumphreys/silverstripe-fixtures

Thanks for the suggestion. Can you elaborate on what that solves @adrhumphreys ?

@adrhumphreys
Copy link
Contributor Author

@jakxnz just need to find time to update the autocomplete code a bit 🙏🏽
The module solves the issue of needing to have a lot of specificity in the order things need to be loaded and increasing the ease of adding in functionality to that creation. I also found often you'll not have put a correct value in, say you have a default value set on write, that doesn't get automatically set anymore so you need to specify it

You end up with these hard to read yml files that sort of go like:

create the elemental area for the page
create the elemental area for the layout block
create the page and link to the area
create the carousel block and link to the area

Where it becomes more difficult to conceptualise what is being done in the populate config

The idea with the other module is you create fixtures in PHP in which you call the normal creation methods that you have in Silverstripe. If you want to create 50 objects with random numbers you can just write a loop to do that.
It manages the loading order by allowing the developer to explicitly state the dependencies for a fixture rather than manually ordering them
It also provides a few purging options, you can choose any/all of the three, array of classes to delete, array of tables to delete, a method called unload which you can write your own code in

This isn't all smooth though as you'll have a performance hit since you're calling all the after writes etc for your models now when populating them. I just found the tradeoff worth it

@jakxnz
Copy link
Contributor

jakxnz commented Sep 11, 2020

The module solves the issue of needing to have a lot of specificity in the order things need to be loaded and increasing the ease of adding in functionality to that creation

Nice. Yep, I have experienced this. Cool that you're working on an enhancement! @adrhumphreys


For anyone looking for a way to support Fluent via populate, I raised a PR against @adrhumphreys fork, with some adjustments that worked for our project adrhumphreys#1

README.md Outdated Show resolved Hide resolved
self::truncateTable($table);
$tables = [];

// All acenstors or children with tables
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo in 'ancestors'

@madmatt
Copy link
Member

madmatt commented Oct 11, 2021

Hey @adrhumphreys there's a few outstanding changes suggested on this, do you still want to look at merging it in at some point?

I'm just trying to work out which order to merge changes in - whether merging #26, #32 then #33 makes the most sense or something else...

@adrhumphreys
Copy link
Contributor Author

Hey @madmatt I haven't actually used the module since I made these changes. I do know there's some internal project(s) using the changes as a fork though so I imagine it's probably higher on the list of wanting to merge in

@jakxnz
Copy link
Contributor

jakxnz commented Oct 12, 2021

@madmatt the suggestions are grammar changes and a disputed suggestion about a method name. Are they a trivial reason to leave a PR on hiatus?

The track record demonstrates that the people who progressed the contribution this far haven't had the capacity to address the outstanding nitpicks. As far as I understand it, there's greater internal benefit to having this merged than leaving it as a liability when support is needed for projects that implement it. Is there value enough there for you to get this merged @madmatt ? Or do we need to wait until the contributors of this PR (@adrhumphreys or I) have capacity?

@madmatt
Copy link
Member

madmatt commented Oct 13, 2021

I've fixed all the conflicts in README.md and rebased/merged the master branch in.

Any final thoughts @jakxnz, @adrhumphreys, @michalkleiner or @chrispenny?

If not, I'm happy to merge it now and we can tidy things up later.

One question I have is how does it actually support fluent now @adrhumphreys? I see there is some old code that supported it by checking if the Fluent extension was applied to an object that was being truncated, but this appears to have been removed. I don't see anything added to the fluent module either, so does it just handle it implicitly now and you didn't have to do anything? Or am I missing something obvious here? 🙂

@madmatt
Copy link
Member

madmatt commented Oct 13, 2021

FWIW, I've made some tweaks to README:

  • Re-instate the warning about not installing in production and make it much clearer. While there is a check in the code to ensure it's only running on dev or test environments, that's not enough (you could fool it with isDev=1, and I've seen many production sites running in dev mode accidentally). So best to reinforce the 'install with --dev' message here.
  • Adjust messaging around Versioned and Fluent to be clearer - on the assumption that this does support Fluent (see my previous comment as I can't understand how that works).

@michalkleiner
Copy link
Contributor

All good with me, all the remaining stuff is quite minor.

@jakxnz
Copy link
Contributor

jakxnz commented Oct 13, 2021

@madmatt , it currently "supports" fluent by exposing an extension point that can be used to clear tables created by other modules e.g

PopulateExtension.php:

/**
 * @param array $tables
 * @param string $className
 * @param array $classTables e.g ['Class\Name' => 'Table']
 */
public function updateTruncateObjectTables(&$tables, &$className, &$classTables)
{
    // Update truncate object tables
    foreach ($classTables as $className => $baseTable) {
        /** @var DataObject|Versioned|FluentExtension $obj */
        $obj = Injector::inst()->get($className);

        if (!$obj->hasExtension(FluentExtension::class)) {
            // No localised tables to clear
            continue;
        }

        $fluentTable = $obj->getLocalisedTable($baseTable);

        $tables[$fluentTable] = $fluentTable;

        if (!$obj->hasExtension(Versioned::class)) {
            // No localised versioned tables to clear
            continue;
        }

        $stages = $obj->getVersionedStages();

        foreach ($stages as $stage) {
            $table = $obj->stageTable($fluentTable, $stage);

            // Include localised base and live table
            $tables[$table] = $table;
        }

        $versionedTable = "{$fluentTable}_Versions";

        // Include localised versions table
        $tables[$versionedTable] = $versionedTable;
    }
}

I felt this approach was less opinionated and had better grouping of concerns.

The title of the PR is a bit misleading.

/**
* @var string
*/
private static $segment = 'populate-task';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a heads up, I'm going to remove this. It breaks BC as lot of projects will have defined composer scripts to the original dev/task path.

Copy link
Collaborator

@chrispenny chrispenny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folks seem happy enough with the changes here. I've tested it on one of my [mammoth sized] projects, and it ran without issue.

If there are any further improvements, please don't hesitate to raise a new PR. I'm going to do my best to keep on top of things going forward.

@chrispenny chrispenny merged commit cdf58a5 into silverstripe:master Oct 20, 2021
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Extensible;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\YamlFixture;
use SilverStripe\ORM\Connect\DatabaseException;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\Versioned\Versioned;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: It's plausible to use this module without the admin module (and/or the versioned module). So while this is a safe assumption (I'm guessing 99.9% of framework-based projects run admin), are we happy to make this assumption?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you'd already merged it. I overlooked that, sorry.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PHP allows you to reference classes (that don't exist) in this way without it breaking. Breakages would only occur when you attempt to use that class. My assumption is that that case is already covered, as all I did was change the reference.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see. I had assumed if (!$obj->hasExtension(Versioned::class)) { is invoking the class, and so would throw an error

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be fair, I'm not not 100% sure about older versions of Extensible, but currently it's just running an is_a() on the instance and extension name.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... Just noticed that Versioned is required. So we're good in any case.

Screen Shot 2021-10-21 at 9 54 45 AM

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦🏻 I probably should have checked that 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants