Kohana 3.x ORM has_many through delete relationship - orm

I Have three tables, contact, list and listmembers. Contacts from contact table are associated to lists from list table via listmembers table.
class Model_Contact extends ORM{
protected $_has_many = array(
'lists'=>array('model'=>'List', 'through'=>'listmembers', 'far_key'=>'dlid', 'foreign_key'=>'uid')
);
}
class Model_List extends ORM
{
protected $_has_many = array(
'contacts'=>array('model'=>'Contact', 'through'=>'listmembers', 'far_key'=>'uid', 'foreign_key'=>'dlid')
);
}
I have to update contact and list relationship in listmemebers table
- create new relationship between existing contact and existing list
- Remove relationship between contact and list
How can I achieve this in Kohana ORM? I can always create model for listmembers and directly add/delete on this model. But is there a way to handle via relationship without creating listmembers model?

I think the documentation explains it quite well: http://kohanaframework.org/3.2/guide/orm/relationships#hasmany-through

Related

Many to Many relationship creation for existing object in mongodb

Two classes:
class Investor
field :name
has_and_belongs_to_many :users
end
class User
field :name
has_and_belongs_to_many :investors
end
I already have users and investors I don't want to create new. What is the way of inclusing like
#user=User.first
#investor=Investor.first
#inves_sec=Investor.last
Now what is the way to add #investor and #invest_sec to the user investors id, all the command I found in mongoid doc create new investor object, I wasn't able to find thorugh which you can use existing object.
It's easy enough as you could do something like:
#first_investor = Investor.first
#user.investors.push(#first_investor)
There are a lots of examples here of various scenarios dealing with relationships/references.

NHibernate not inserting child entities in one-to-many

I have Customer and Profile classes, where one Customer can have many Profiles.
I am using following NHibernate override classes with them:
public void Override(AutoMapping<Customer> mapping)
{
mapping.Table("[Customer]");
mapping.Id(x => x.Id, "Id").GeneratedBy.Identity();
mapping.HasMany(x => x.Profiles).Cascade.All().Inverse();
mapping.Map(x => x.FirstName, "FirstName");
mapping.Map(x => x.LastName, "LastName");
mapping.Map(x => x.Email, "Email");
}
public void Override(AutoMapping<Profile> mapping)
{
mapping.Table("[Profile]");
mapping.Id(x => x.Id, "Id").GeneratedBy.Identity();
mapping.References(x => x.Customer, "Customer_Id").Cascade.None();
mapping.Map(x => x.FacebookProfileLink, "FacebookProfileLink");
mapping.Map(x => x.ContactPhone, "ContactPhone");
}
I am getting following error while inserting Profile object:
Cannot insert the value NULL into column 'Customer_Id', table 'dbo.Profile'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated.
My intence is to insert Customer object before Profile that needs a reference to Customer
object. That's why I'm using Inverse attribute. Unfortunately it doesn't work. Any help on this ? Thank you.
One thing that NHibernate does as a good ORM is to save everything in the relationships so you don't have to save things separately. I think your issue might but in what you are doing when you say 'insert Customer object before Profile that needs a reference to Customer'.
The reality is that you should create the customer, with the profiles needed to be associated to it, and then just save the customer. NHibernate will save all the entities in the right order to make sure the relationships are preserved. Try doing that, first create or retrieve the customer entity, then add/remove the profiles, and then save the customer. Profiles would be saved because of the cascade option you have specified in the mapping.
Hope that helps!
I found out solution that works for me.
I am saving Customer entity without reference to it's Profiles. Afterwards I'm saving Profile entity.
Works for me even better than solution i wanted to achieve before, as I can check on success of Customer insert and then decide what to do next (save Profile as well or do some validation error).
Thank you all for your answers.

Yii HAS_ONE relation doesn't save foreign key

I have a problem with HAS_ONE relation in Yii framework. The scenario is as follow:
We have a User class with relation to SubscriptionType:
'subscriptionType' => array(self::HAS_ONE, 'SubscriptionType', 'subscription_type_id')
And s SubscriptioType with relation to User:
'users' => array(self::HAS_MANY, 'User', 'subscription_type_id')
What is more, User has a Foreign Key to SubscriptionType defined in the database.
There are 3 subscription types predefined and all the registering users get one of them by default during the registration. They are saved in the DB, so in the registerAction I do:
//some assignments here
$subscription = SubscriptionType::model()->find('name=:name', array(':name'=>SubscriptionType::MONTHLY));
$newUser->subscriptionType = $subscription;
if($newUser->save()){
//redirect to some page
} else {
Yii::trace('User register failed', 'application.controllers.UserController');
}
The user don't get saved. I debuged it bit and I noticed, that subscriptionType is assigned but subscription_type_id is not, so the INSERT query is throwing the constraint violation.
Do I have to set the subscription_type_id explicitly? It doesnt make to much sense to me because it's against the idea of ORM, isnt' it?
I think you are defining the relations incorrectly. HAS_ONE is for the Parent side of a One-to-One relationship, and HAS_MANY is for the Parent side of a One-to-Many relationship. You need a BELONGS_TO on one of these which should be on the child side of the relationship. I am guessing that you user model should have the BELONGS_TO like this:
'subscriptionType' => array(self::BELONGS_TO, 'SubscriptionType', 'subscription_type_id')
The other issue you are having is that you are trying to assign a value to subscripionType when it is more like a read only attribute in that it is populated by the framework. In the case of a HAS_ONE, or a BELONGS_TO, this will be a CActiveRecord model. In the case of a HAS_MANY or MANY_TO_MANY, it will be an array of CActiveRecord models. These models are not saved when you save the parent model. However since they are indeed models, you can update and save these individual child models.

Yii Model - MANY_MANY relations - do I still need a HAS_MANY?

I am defining the many to many relationship between two objects (ModelA & ModelB for this example) through three tables/active record models in the following way:
ModelA --< ModelA_B >-- ModelB
Where ModelA_B contains a foreign key field to both ModelA and ModelB. So in the code for ModelA I have have added to the relations() function:
'modelbs' => array(self::MANY_MANY, 'ModelB', 'tbl_modelb(modela_id,modelb_id)'),
My question is do I still need the HAS_MANY relationship that was generated by Gii to represent the relation to the linking table ModelA_B or is this declared implicitly by the MANY_MANY above?
'modelabs' => array(self::HAS_MANY, 'ModelA_B', 'ModelA_Id'),
If you use a MANY_MANY relation, you don't need to define another HAS_MANY relation for the ModelA_B table.
But you could also use the through feature, which will replace the MANY_MANY relation at some point (probably in Yii 2.0 if i remember right). In this case you would define 2 relations:
'mobelabs' => array(self::HAS_MANY, 'ModelA_B', 'ModelA_Id'),
'modelbs' => array(self::HAS_MANY, 'Model_B', 'ModelB_Id', 'through'=>'modelabs'),
Now you have access to both related records: the ModelA_B via $modelA->modelabs and the ModelB via the $modelA->modelbs.

How to Delete in a many to many relationship?

I have a many to many relationship:
Product has many Categories and Category has Many Products.
Say I have
Shopping Category
Food Category
Product A - Shopping Category, Food Category
Product B - Shopping Category
Now I delete Shopping Category. I want the Product A reference to be removed from Shopping Category and I want Product B to be removed completely.
I would end up with:
Product A - Food Category.
How do I do this in nhibernate (I am using fluent nhibernate).
I tried to use Cascade DeleteOrphan and AllDeleteOrphan but when I do that and delete Shopping both Product A and B get deleted.
public class CategoryMapping : ClassMap<Category>
{
public CategoryMapping()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
HasManyToMany(x => x.Products).Cascade.DeleteOrphan();
}
}
public class ProductMapping : ClassMap<Product>
{
public ProductMapping()
{
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).Not.Nullable().NvarcharWithMaxSize();
HasManyToMany(x => x.Categories);
}
}
unitOfWork.BeginTransaction();
Category category =session.Load<Category>(id);
session.Delete(category);
unitOfWork.Commit();
I don't think this can be handled by mapping with existing data structure. I think you would need to write some manual code (*) or change data structure.
(*) Not 100% sure it works though...
unitOfWork.BeginTransaction();
Category category =session.Load<Category>(id);
var productsDel = category.Products.Where(p => p.Categories.Count == 1);
productsDel.ForEach(p => session.Delete(p));
session.Delete(category);
unitOfWork.Commit();
Other:
I'm also thinking about adding mapping for your cross-ref tables. Then you should be able to configure mapping so it will delete only records from that cross-ref table. You will need to verify if there are products without references and delete them periodically. (some periodic clean-up code, like running some stored procedure). I know this solutions smells bad :) There are still triggers and other SQL Server stuff... not good solutions anyway, but solutions.
If you just want to remove the association between the two use Cascade.SaveUpdate()
Then just remove the entity from the collection and commit the transaction if you are using transactions if not you will need to do a Session.Flush