zanderwar
zanderwar

Reputation: 3730

has_many confusion using silverstripe ORM

Firstly, forgive my scrubbiness and lack of experience in third-party ORM's (have gone too long "tailor-making" requirements)

I am having a difficult time understanding relations in SilverStripe ORM

What I'm trying to achieve is to correlate a set of attributes to a single RoomTypeId

/mymodule/models/Rooms.php

class Rooms extends DataObject
{

    private static $db = array(
        "RoomTypeId"        => 'Int', // This needs to correlate with Attributes.RoomTypeId
        "RoomTypeGroupId"   => 'Int',
        "SubPropertyId"     => 'Int',
        "Name"              => 'Varchar(255)',
        "Description"       => 'Text',
        "MaxOccupancy"      => 'Int',
        "Raw"               => 'Int',
        "NoDiscountAllowed" => 'Int',
        "DefaultArrival"    => 'Varchar(10)',
        "DefaultDeparture"  => 'Varchar(10)',
        "Image1"            => 'Varchar(255)',
        "Image2"            => 'Varchar(255)',
        "Image3"            => 'Varchar(255)',
        "Image4"            => 'Varchar(255)',
        "Image5"            => 'Varchar(255)',
        "MinCost"           => 'Currency'
    );

    public static $has_many = array(
        "Attributes" => "Attribute"
    );

}

/mymodule/models/Attributes.php

class Attributes extends DataObject {

    private static $db = array(
        "RoomTypeId" => 'Int', // needs to correlate with Room.RoomTypeId
        "AttributeId" => 'Int',
        "Name" => 'Varchar(255)'
    );

    private static $has_one = array(
        "Rooms" => "Rooms"
    );

}

However the docs say that a column is added to the DB <relation-name>ID which does not conform to my need to correlate multiple attributes to Rooms.RoomTypeId

Due to my misunderstanding and time limitations I've literally been

Rooms::get()->filter([...])
Attributes::get()->filter([...])

Which is all well and good on the PHP side of things but non-plausible from the context of the template.

Any guidance highly appreciated!

Note: RoomTypeId is retrieved from an external API

Upvotes: 0

Views: 279

Answers (1)

asbha
asbha

Reputation: 21

I believe your confusion may be stemming from treating the objects as full SQL tables - this is not the case. The ORM is to allow the developer to work in OOP terms, and not have to particularly worry about how the information is stored.

I hope I'm not wrong (your description is rather brief), but it seems that you're basically just doing too much rather than anything particularly too wrong.

Each relation in SilverStripe generally requires a reverse to be set. It is not strictly required in has_one or many_many, but is in has_many. This is to do with the way SilverStripe inspects your model information and generates the storage for you (cf. dev/build).

As you seem particularly clued in SQL, I'll jump straight to technical explanations:

  • $db are for the Object's native properties, not for relational data.
  • A has_one will store an external ID on the object's table, in the form <relationship name>ID, i.e. "Attributes"."RoomsID". This is generated for you.
  • A has_many will store the object's own ID on the relation's table. This is not generated for you, and thus requires a has_one on the related class (as you have in your code demo).
  • A many_many stores relations in their own table, matching two external IDs. A reasonably traditional join table. A reverse is not strictly required because of the nature of a join table, but it is a good idea to set the reverse anyway, in the form belongs_many_many. This will let the ORM know that the relationship exists when you're dealing directly with the 'child' objects.

You can see the above bullet points visually explained in the image at http://www.silverstrip.es/blog/diagram-of-relationships-in-silverstripe/

As a side note it looks like you're storing image references as Varchars. This would only be required if the information is kept externally. Iff the images are managed via SilverStripe, a many_many to Image is probably sufficient. Otherwise there are examples of having many images related to an object in lesson seven on the official SilverStripe docs website.

And furthermore, the manual fetch and filter is plausable from the template. Defining a function on your model (or the controller) Rooms::getAttributes() and/or Attributes::getRooms() and returning that get()->filter() sequence will allow the resulting queries to be accessed in the template via $Attributes on Rooms and $Rooms on Attributes, respectively. You should be careful with this though, as it can gives issues with the magic fetch (via php's __call) easily.

Upvotes: 2

Related Questions