Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Data Model Questions /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

[WORKAROUND] Two different many_many and belongs_many_many relationships on the same two tables


Go to End


9 Posts   7687 Views

Avatar
NathanBrauer

Community Member, 11 Posts

21 May 2011 at 10:41am

Edited: 28/05/2011 8:16am

Hello SS World :)

I was stumped on this for a while but I figured it out so I figured I'd share my results.
Update: Nevermind, it doesn't seem to be working. After reading this message, continue to the responses!
Update #2: I found a workaround to get this to work. Make sure you read the whole thread. The workaround is the 7th post.

I was trying to build two classes: Person, Company
A Person can be an Employee of a Company.
A Person can Represent a Company, but not necessarily be an employee of that company.

Both of these relationships would be represented by this table structure (one for each) if it was outside of Silverstripe:

| ID | PersonID | CompanyID |

Now I just needed to get SS to recognize which is which.

// In Person.php
static $belongs_many_many = array(
	'Represents'	=> 'Company',
	'Employers'	=> 'Company',
);

// In Company.php
static $many_many = array(
	'Representatives'=> 'Person',
	'Employees'	 => 'Person',
);

As you can see, now Silverstripe won't know which is which. If I call $Person->Employers(), it won't know whether to look in Company_Representatives or Company_Employees for the right data.

The solution is to use dot notation on the class with <b>belongs_many_many</b> (and <u>NOT</u> the other) where the added parameter would be the reference key from many_many.

// In Person.php
static $belongs_many_many = array(
	'Represents'	=> 'Company.Representatives',
	'Employers'	=> 'Company.Employees',
);

// In Company.php
static $many_many = array(
	'Representatives'=> 'Person',
	'Employees'	 => 'Person',
);

P.S. This should be added to the docs. A dual has_one/has_many relationship is described there but it's not very clear and it doesn't state how to do it with many_many. :)

Avatar
swaiba

Forum Moderator, 1899 Posts

21 May 2011 at 2:01pm

wow - thank you so much for the post, I've hacked my way around this a couple of times - but now I have the elegant solution! :)

Avatar
danzzz

Community Member, 175 Posts

24 May 2011 at 12:44am

Edited: 24/05/2011 12:45am

hi nathan,

I tried this, but get an error ...

I have a DataObject "Shop", where I save webshops. Each webshop can be in many countries and can deliver to many countries.

here my code:

Country.php

class Country extends DataObject {
static $many_many = array(
        'Shops' => 'Shop',
	'DeliveryCountries' => 'Shop'
    );
}

Shop.php

class Shop extends DataObject {
static $belongs_many_many = array(
	'Shops' => 'Shop.Shops',
	'DeliveryCountries' => 'Shop.DeliveryCountries',
    );
}

SS build the tables with success, I have this tables now:

Country
Country_Shops
Country_DeliveryContries

But there is this error:

Error: "Bad class to singleton() - Country.Shops" at line 346 of /var/www/xxx.com/sapphire/core/Core.php

whats wrong here?

thx

Avatar
NathanBrauer

Community Member, 11 Posts

25 May 2011 at 2:48am

Hi @danzzz,

I actually ran into some problems while testing this too. Here's how I fixed it:

// In Person.php 
static $belongs_many_many = array( 
   'Represents'   => 'Company', 
   'Employers'   => 'Company.Employees', 
);

// In Company.php 
static $many_many = array( 
   'Representatives'=> 'Person', 
   'Employees'    => 'Person', 
);

What changed?
In the first $belongs_many_many attribute, I used only the class/table name and did not include the identifier.

Danzzz, try that. It does sound like your problem may be different though. If that doesn't work you may not be able to do it with a Singleton class.

Avatar
NathanBrauer

Community Member, 11 Posts

25 May 2011 at 6:24am

Ok, I take it back.
Everything was working until I tried to access Person->Employers (error I got was: Unknown class passed as parameter -- see trace below)

swaiba, were you able to get this working?

is_subclass_of(MVEntity.Employees,DataObject) 
Line 568 of Object.php
Object::prepare_statics(MVEntity.Employees) 
Line 381 of Object.php
Object::uninherited_static(MVEntity.Employees,many_many) 
Line 1745 of DataObject.php
DataObject->many_many(ReportsTo) 
Line 1374 of DataObject.php
DataObject->getManyManyComponents(ReportsTo) 
call_user_func_array(Array,Array) 
Line 711 of Object.php
Object->__call(ReportsTo,Array) 
Line 86 of Page.php
Person->Employers() 
Line 86 of Page.php

Avatar
swaiba

Forum Moderator, 1899 Posts

25 May 2011 at 7:42am

as much as I liked it - I haven't tried it.

I want it to work and yet I've had a work around for a while and it's just not elegant (use Text in db array, use it to store an id csv list, edit with a custom checkboxfieldset).

Avatar
NathanBrauer

Community Member, 11 Posts

28 May 2011 at 8:14am

Edited: 28/05/2011 8:17am

Alright, I couldn't figure out how to fix it, but I found out a hack that gets almost the exact same results

This is what should work but doesn't:

// In Person.php 
static $belongs_many_many = array( 
'Represents' => 'Company.Representatives', 
'Employers' => 'Company.Employees', 
);

// In Company.php 
static $many_many = array( 
'Representatives'=> 'Person', 
'Employees' => 'Person', 
);

This is an almost seemless workaround that DOES work:

// In Person.php 
static $belongs_many_many = array( 
    'Represents' => 'Company'
);
static $many_many = array( 
    'Employers'=> 'Company'
);

// In Company.php 
static $many_many = array( 
    'Representatives'=> 'Person'
);
static $belongs_many_many = array( 
    'Employees' => 'Person'
);

This basically has the results we want (Company->Representatives(), Company->Employees(), Person->Represents(), and Person->Employers() all work properly) but instead of having the tables Company_Representatives and Company_Employees, we have Company_Representatives and Person_Employers. Except for that difference, everything else should work exactly the same.

Avatar
NathanBrauer

Community Member, 11 Posts

28 May 2011 at 8:17am

Let me know if that works for you too :)

Go to Top