0
votes

I want to create a class that extends DataObject and simply has a Title, Desc, and Image.

class ImageBlock extends DataObject
{
    private static $db = [
        'Title' => 'Varchar(50)',
        'Description' => 'Varchar(255)'
    ];

    private static $has_one = [
        'Image' => 'Image'
    ];
}

This is generic Tile to display on the Frontend and could be shown on multiple pages and within multiple DataObjects. A given page or DO can have many of these. To clarify, this is not just for pages. I have a Region DO that has_many of these ImageBlocks:

class TourRegion extends \DataObject
{
    private static $db = [
        'RegionName' => 'Varchar(50)',
        'RegionSlug' => 'Varchar(50)',
        'RegionIntro' => 'Varchar(255)',
        'RegionDescription' => 'Text',
    ];

    private static $has_many = [
        'RegionHeroImages' => 'TourHeroImage',
        'MainAttractions' => 'ImageBlock'
    ];

    ....

My question is, a has_many to a DataObject requires a has_one relationship on that DataObject. Since the has_one relationship could be more than one possible class, how do I create this reference?

I have tried adding a has_one to the lowest common class that these objects share (DataObject) as follows:

class ImageBlock extends DataObject
{
    private static $db = [
        'Title' => 'Varchar(50)',
        'Description' => 'Varchar(255)'
    ];

    private static $has_one = [
        'Image' => 'Image',
        'ParentObject' => 'DataObject'
    ];
}

But I get this error:

[User Error] Uncaught Exception: No has_one found on class 'ImageBlock', the has_many relation from 'TourRegion' to 'ImageBlock' requires a has_one on 'ImageBlock'

I get the same error when I try to omit this has_one on ImageBlock altogether. Which begs the question; Why is it I can add has_many relationships to DataObjects like Image or File without the Image or File class having a has_one reference to my Object?

It seems that it's not possible to have generic and reusable has_many related objects in Silverstripe. And that every class that needs to have this ImageBlock must duplicate the class for the sole purpose of adding the has_one reference.

1

1 Answers

2
votes

To answer the last part of your question, it's important to remember that has_many is schematically meaningless. It imposes no structural changes to your database. All it does is add a magic method to the parent DataObject that looks for a has_one somewhere else. Defining a has_many is basically just for convenience, to save you the time of writing a getter.

If you're looking to define the relation in the parent, which to me makes sense, I would do that as a many_many, as that requires no reciprocity (It can be reciprocated by belongs_many_many, but that is just a convenience method, too).

For consistency and clarity, I would create an extension to inject the many_many => ImageBlock to the DO's that want it.