Yii HAS_ONE relation doesn't save foreign key - yii

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.

Related

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.

Relationships in Model Ruby on Rails

I have a View called View_CrossReference in Sybase. I want to create Model for this view.
This table has a column called Answer.
I have an another table and model called SurveyXref. In this table i have a column
called Answer_No.
I want to create a relationship between these models and get the data from Answer column from View_CrossReference and display into my grid in SurveyXref page.
IN view_crossreference model
has_one :survey_xrefs, :primary_key => :answer, :foreign_key =>
:answer_no
In survey_xrefs model
belongs_to :view_crossreference, :primary_key => :answer ,
:foreign_key => :answer_no
now simply in controller u can access
v.view_crossreference.answer
Without "Model", means you have created table via stand alone migration.
Could you please show me your code for better understanding?

rails scope and joins

I have tried everything i thought would work for this and am turning up nothing.
in rails 3, I need to find all users with a cd player in their car. A car has one user and one radio, and a user belongs to a car, and a radio has many cars.
I am stumbling on how I would perform this search via a scope in the user model.
class User
belongs_to :car
class Car
belongs_to radio
has_one :user, :dependent => destroy
class Radio
has_many :cars
I am assuming that you mean this:
Car has radio_id, User has car_id,
since a radio has many cars and car has one user. The table with the foreign key always is on the belongs_to end of the relationship.
Without really knowing the structure you're looking for, something like the following should work:
scope :with_cd_player, joins(:cars).where('cars.radio_id is not null')
if there is a category column on the radio, the following would work.
scope :with_cd_player, joins(:car => :radio).where('cars.radio_id is not null').where("radios.category = 'cd_player'")
For Rails Version >= 4:
scope :with_cd_player, -> { joins(:cars).where.not(cars: { radio_id: nil }) }
Also you can using "merge"
https://gorails.com/blog/activerecord-merge

Has_many :through association

I made a relationship with the three models using has_many :through:
class Curriculum class < ActiveRecord::Base
has_many :interests
has_many :vacancies,: through => :interests
end
class Vacancy class < ActiveRecord::Base
has_many :interests
has_many :resumes,: through => :interests
end
class Interest < ActiveRecord:: Base
belongs_to :vacancy
belongs_to :curriculum
end
And to create curriculum and vacancy, I create them by administrative, i need to know how can i create the interest to the id of the vacancy, and how it will be logged on the system I have to get the id of it and make the relationship in creating a new bank interest. I wonder how I can program it to do so, and I wonder how the controller will get the create action, and what better way to do this.
First, try to read the whole "Guide to Rails on Associations", especially the part about has_many :through. Then check your schema if your db is migrated and contains for the table interests the necessary foreign keys to curriculums and vacancies called curriculum_id and vacancy_id.
If that is all in place, the following code will create the relationship between two objects:
#curr = Curriculum.find(1)
#vac = Vacancy.find(1)
#curr.interests << #vac
#curr.save
The last two lines creates an interest between #curr and #vac and store that on the database. So you should not use IDs and handle them directly, but work with objects instead.
The second part now is to provide a UI to allow the definition (and removal) of interests between curricula and vacancies. The base flow here is:
You have one curriculum in focus.
You have a link to add / remove curricula.
The view that opens shows a list of possible vacancies, where every vacancy has a checkbox.
By selecting (or deselecting) the check boxes, the IDs of the vacancies will be held in the params of the request sent to the controller.
See the (older) podcast Railscast #52 how to do that in a similar context. Or see the example for has_many :through with checkboxes.
An alternative way would be to use JQuery autocomplete, and add so interests one-by-one. See the nice podcast Railscast #258 which uses JQuery Tokeninput for that.
I think this is what your looking for:
HABTM Checkboxes
That's the best way to use an Has and Belongs to many association.

Using attr_accessible in a join model with has_many :through relationship

I have a USER that creates a COMPANY and become an EMPLOYEE in the process. The employees table has an :user_id and a :company_id.
class User
has_many :employees
has_many :companies, :through => :employees
class Employee
belongs_to :user
belongs_to :company
attr_accessible :active
class Company
has_many :employees
has_many :users, :through => employees
Pretty basic. But here's the thing, the resource EMPLOYEE has other attributes than its foreign keys, like the boolean :active. I would like to use attr_accessible, but this causes some problems. The attribute :user_id is set right, but :company_id is nil.
#user.companies << Company.new(...)
Employee id:1 user_id:1 company_id:nil
So my question is: if :user_id is set right, despite it is not an attr_accessible, why :company_id isn't set right just the same? It shouldn't be an attr_accessible.
I'm using Rails 3.0.8, and have also tested with 3.0.7.
There are a lot of bits working together here.
You definitely want to use attr_accessible on all models. (Google "hack rails mass assignment" and read the Rails Guide on mass assignment.)
Once you add attr_accessible to a model, all assignments from hashes (mass assignments) are disabled except those you explicitly allow. However, you can still assign values directly, one at a time.
Foreign keys seem like a good thing to exclude from mass assignment, so don't list them in attr_accessible.
The .create and .build methods are not using mass assignment so they can set the value of one foreign key association. If there are several associations, as best I can tell, you'll have to set all but the first separately.
Finally, the actual IDs for the foreign keys are created by the database, not by ActiveRecord. So you'll either have to create parent and child records simultaneously, or you'll have to save the child first before you can assign the foreign key in the parent. Otherwise there is no ID available for the assignment.
It's not clear to me from your example how Employee is getting instantiated. But since the Employee belongs to both User and Company, I think something like this might work, assuming #user already exists:
company = #user.companies.create(..) # fills in company.user_id and saves to DB
employee = #user.employees.build(..) # fills in employee.user_id but does NOT save yet
employee.company = company # fills in employee.company_id
employee.save # now save to DB
The company_id is nil simply because the Company hasn't been saved to the database yet - Company.new simply creates the object in memory without saving it yet.
If you do:
#user.companies << Company.create(..)
or
#user.companies << Company.first
They should both work. There's even a shorter method which I think should work too:
#user.companies.create(..)
It all depends at which point you want to save the association. In some cases, it may be better not to save the employee and company models straight away, and instead wait for when the parent model (User) is saved. In which case you can use:
#user.companies.build(..)
(which is similar to the code in your example).
In terms of your active boolean attribute on the Employee model, if this is a column in the database, you don't need to explicitly declare attr_accessible for it - it'll be accessible by default.