Kohana 3.3 ORM 'has_many' and 'through' case problems with different model and table name - kohana-3.3

I can't seem to get the ORM "has_many" and "through" case working when I use PSR-0 model names that have more than one word because my model is User_Business_Unit and my table is user_business_unit. For example:
Here is the has_many relationship in my 'User' model:
protected $_has_many = array(
'business_unit' => array(
'model' => 'Business_Unit',
'through' => 'user_business_unit',
'foreign_key' => 'user_id',
'far_key' => 'business_unit_id',
);
If I do a
echo $user->has('business_unit', 2);
for example, it will work. But if I do a
$user->add('business_unit', 2);
it fails because it tries to instantiate the model with the 'through' alias "user_business_unit", but the model is "User_Business_Unit".
If I change my 'through' setting to "User_Business_Unit" then the add() case works but the has() case fails because it tries to query the table "User_Business_Unit" but the table is "user_business_unit".
Can someone tell me what I am doing wrong here?
I suppose I could rename my table to be "User_Business_Unit", but that hardly seems like a good solution.

Related

Rails 4 delete association for empty nested attributes when updating parent

I have a model House that belongs to a User. House has_many cars. In my models, i've declared the necessary accepts_nested_attributes_for, and my nested attributes have the appropriate _attributes and id fields.
I want to be able to disassociate my Car model from House, but not User.
Let's say I have a saved House object like this:
"house" => {"owner" => "bob, "car_attributes" => [{"id" => 1, "type" => "toyota"},{"id" => 2, "type" => "honda"}]}
I want to remove (but not delete) a car object from House and update it so it looks like this:
"house" => {"owner" => "bob, "car_attributes" => [{"id" => 1, "type" => "toyota"}]}
Obviously when I call #house.update_attributes({"owner" => "bob, "car_attributes" => [{"id" => 1, "type" => "toyota"}]}), the belongs_to association is not removed. Is there any simple way to disassociate a car object from its parent?
Looking through the Rails documentation, there is a collection method delete that does just that -i.e sets the parent foreign keys to null. The problem is, I would have to mark each object I want to dissassociate in the client, send it over, then walk through each nested attribute and call delete() manually where necessary, which doesn't seem very railsy to me.
Is there an easier way to update a parent model by disassociating (without destroying) its nested attributes in rails?

Factory Girl with implicit has_many association

I'm trying to test a model that has an implicit has_many association, and am having some difficulties.
I have a table A, with column B_ID, where B is basically a foreign key - except there is no table B in my database, or an active record class associated with object B. There is also table C that has column a B_ID.
In the model for table C we have:
# implicit has_many :alphas
def alphas
Alpha.where(:b_id => b_id).order(:xyz)
end
This database structure makes sense for the data I have, and the non-test code works fine.
My test code almost works, and I hope I am just missing something simple.
I have factories defined for A and C, and I have a test:
a1 = Factory(:alpha, :b_id => 123, :xyz => 100)
a2 = Factory(:alpha, :b_id => 123, :xyz => 200)
c1 = Factory(:c, :b_id => 123)
puts c1.alphas.count
puts c1.alphas.first
c1.alphas.first.should == a1
The output is:
2
nil
<test fails>
Changing the number of A objects that share the B_ID result in the c1.alphas.count changing, but I can't seem to actually inside the implicit association and get an A object back - instead I always get nil. There are other methods that in my C model that I can't test because those methods need to access fields on individual A objects.
Does anybody have any insight into what is going behind the scenes here, or what I might do to get around this? Thanks.
Take a look at this for an example to have an admin_user with the role of Admin.
https://github.com/drhenner/ror_ecommerce/blob/master/spec/factories/user.rb
In this case roles are not a factory.
I find it best to do this like the following though.
#order = Factory(:order)
order_item = Factory(:order_item, :total => 5.52 )
#order.stubs(:order_items).returns([order_item, order_item])
or
#order = Factory(:order)
order_item = Factory(:order_item, :total => 5.52, :order => #order )

Polymorphic Relationship Table Queries in Rails — find object by multiple

I have a relationship table in a rails application called edit_privileges, in which the User is the "editor" and a number of other classes are "editable". Let's say that two of those classes are Message and Comment.
My EditPrivilege model uses the following code:
belongs_to :editor, :class_name => "User"
belongs_to :editable, :polymorphic => true
And User, of course
has_many :edit_privileges, :foreign_key => "editor_id"
In order to determine if a user has edit privileges for a certain model, I can't do the normal query:
user.edit_privileges.find_by_editable_id(#message.id)
because if the user has edit privileges to edit a comment with the same id as #message, the query will return true with the wrong edit privilege record from the table.
So, I tried doing these options:
user.edit_privileges.find(:all, :conditions => ["editable_id = ? AND editable_type ?", #message.id, #message.class.to_s])
user.edit_privileges.where(:editable_id => #message.id, :editable_type => #message.class.to_s)
which works great at finding the right record, but returns an array instead of an object (an empty array [] if there is no edit privilege). This is especially problematic if I'm trying to create a method to destroy edit privileges, since you can't pass .destroy on an array.
I figure appending .first to the two above solutions returns the first object and nil if the result of the query is an empty has, but is that really the best way to do it? Are there any problems with doing it this way? (like, instead of using dynamic attribute-based finders like find_by_editabe_id_and_editable_type)
Use find(:first, ...) instead of find(:all, ...) to get one record (note it might return nil while find will raise an RecordNotFound exception). So for your example:
user.edit_privileges.find(:first, :conditions => { :editable_id => #message.id, :editable_type => #message.class.to_s })
BTW, if you're on more edge rails version (3.x), Model.where(...).first is the new syntax:
user.edit_privileges.where(:editable_id => #message.id, :editable_type => #message.class.to_s).first

Searching with thinking_sphinx and filtering results

I have this scenario where I thought it would be pretty basic, but found out that I can't really achieve what I need. This is why I have this question for a thinking_sphinx's expert.
The scenario is this: I need do a search within a list of companies and only return those who has an address (there can be many address by company) which belongs to a particular city or none at all (this I can do).
I have the following models :
class Company < ActiveRecord::Base
has_many :company_addresses
define_index
indexes :name
indexes :description
indexes :keywords
end
end
and
class CompanyAddress < ActiveRecord::Base
end
The CompanyAddress has a city_id property. Without looping through all returned records from a sphinx search, is there a way to achieve the same thing more easily?
I'm using Rails 3.0.3 and thinking_sphinx.
You'll want to add an attribute pointing to the city_id values for the company:
has company_addresses.city_id, :as => :city_ids
And then you can filter on Companies belonging to a specific city:
Company.search 'foo', :with => {:city_ids => #city.id}
If you want both matching to a specific city or has no cities, that's a little trickier, as OR logic for attribute filters is more than a little tricky at best. Ideally what you want is a single attribute that contains either 0, or all city ids. Doing this depends on your database, as MySQL and Postgres functions vary.
As a rough idea, though - this might work in MySQL:
has "IF(COUNT(city_id) = 0, '0', GROUP_CONCAT(city_id SEPARATOR ',')",
:as => :city_ids, :type => :multi
Postgres is reasonably similar, though you may need to use a CASE statement instead of IF, and you'll definitely want to use a couple of functions for the group concatenation:
array_to_string(array_accum(city_id, '0')), ',')
(array_accum is provided by Thinking Sphinx, as there was no direct equivalent of GROUP_CONCAT in PostgreSQL).
Anyway, if you need this approach, and get the SQL all figured out, then your query looks something like:
Company.search 'foo', :with => {:city_ids => [0, #city.id]}
This will match on either 0 (representing no cities), or the specific city.
Finally: if you don't reference the company_addresses association anywhere in your normal fields and attributes, you'll need to force to join in your define_index:
join company_addresses
Hopefully that provides enough clues - feel free to continue the discussion here or on the Google Group.

How can I get a unique :group of a virtual attribute in rails?

I have several similar models ContactEmail, ContactLetter, etcetera.
Each one belongs_to a Contact
Each contact belongs_to a Company
So, what I did was create a virtual attribute for ContactEmail:
def company_name
contact = Contact.find_by_id(self.contact_id)
return contact.company_name
end
Question: How can I get an easy list of all company_name (without duplicates) if I have a set of ContactEmails objects (from a find(:all) method, for example)?
When I try to do a search on ContactEmail.company_name using the statistics gem, for example, I get an error saying that company_name is not a column for ContactEmail.
Assuming your ContactEmail set is in #contact_emails (untested):
#contact_emails.collect { |contact_email| contact_email.company_name }.uniq
You don't need the virtual attribute for this purpose though. ActiveRecord sets up the relationship automatically based on the foreign key, so you could take the company_name method out of the ContactEmail model and do:
#contact_emails.collect { |contact_email| contact_email.contact.company_name }.uniq
Performance could be a consideration for large sets, so you might need to use a more sophisticated SQL query if that's an issue.
EDIT to answer your 2nd question
If company_name is a column, you can do:
ContactEmail.count(:all, :joins => :contact, :group => 'contact.company_name')
On a virtual attribute I think you'd have to retrieve the whole set and use Ruby (untested):
ContactEmail.find(:all, :joins => :contact, :select => 'contacts.company_name').group_by(&:company_name).inject({}) {|hash,result_set| hash.merge(result_set.first=>result_set.last.count)}
but that's not very kind to the next person assigned to maintain your system -- so you're better off working out the query syntax for the .count version and referring to the column itself.

Resources