11

私は現在、4.0.0beta1の下で開発中のプロジェクトで、各ユーザーをエンティティにリンクできるユーザーベースの認証が必要でした。私はちょっとレールに新しく、いくつかのトラブルを抱えていました。has_one throughと複数表の継承によるポリモーフィックな関連

モデルは以下の通りです:

何私が必要とするには、ユーザが(私はエンティティを呼び出すことになりますどのようなものを二つがある代理店またはクライアントのどちらか一方にリンクできるようにするためである
class User < ActiveRecord::Base 
end 

class Agency < ActiveRecord::Base 
end 

class Client < ActiveRecord::Base 
    belongs_to :agency 
end 

)。それはリンクを全く持たず、最大でも1つのリンクを持つことができます。

私が探した最初のものは、レールにMutli-Table inheritance(MTI)を行う方法でした。しかし、いくつかのものは私をブロックされた:

  • それはMTIは、ソリューションを実装する
  • 宝石が古いとすぎるかCOMPLEXE見えたな私と初心者のための実装がちょっとハードに見えたか、箱から出して
  • 利用できませんでした彼らはしばらく

用に更新されていなかったとして完成

  • 宝石は、おそらくrails4下破っただろうではないので、私は別の解決策を見て、私はpolymorphic associationsを見つけました。

    私は昨日から、この上にあるとさえRails polymorphic has_many :throughActiveRecord, has_many :through, and Polymorphic Associations

    の助けを借りてそれを動作させるためにいくつかの時間がかかった私は仕事上の問題から、例を作ることができたが、それはしばらくして私を取りました最後に2つの問題があります:

    1. ユーザーの関係をhas_one関連に変換し、リンクされたエンティティに「盲目的に」アクセスする方法はありますか?
    2. ユーザーが複数のエンティティを持つことができないように制約を設定するにはどうすればよいですか?
    3. 私がしたいことをする良い方法はありますか?

      移行ファイル:

      class CreateUserEntities < ActiveRecord::Migration 
          def change 
          create_table :user_entities do |t| 
           t.integer :user_id 
           t.references :entity, polymorphic: true 
      
           t.timestamps 
          end 
      
          add_index :user_entities, [:user_id, :entity_id, :entity_type] 
          end 
      end 
      

      モデル:

      class User < ActiveRecord::Base 
          has_one :user_entity 
      
          has_one :client, through: :user_entity, source: :entity, source_type: 'Client' 
          has_one :agency, through: :user_entity, source: :entity, source_type: 'Agency' 
      
          def entity 
          self.user_entity.try(:entity) 
          end 
      
          def entity=(newEntity) 
          self.build_user_entity(entity: newEntity) 
          end 
      end 
      
      class UserEntity < ActiveRecord::Base 
          belongs_to :user 
          belongs_to :entity, polymorphic: true 
      
          validates_uniqueness_of :user 
      end 
      
      class Client < ActiveRecord::Base 
          has_many :user_entities, as: :entity 
          has_many :users, through: :user_entities 
      end 
      
      class Agency < ActiveRecord::Base 
          has_many :user_entities, as: :entity 
          has_many :users, through: :user_entities 
      end 
      

      はあなたが私はゲッターと私セッターを追加見ることができるようにここで完全に動作する例を示します

  • 答えて

    11

    「エンティティ」という名前です。 has_one :entity, through: :user_entityは、次のエラーを発生させているためです。

    ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'User#entity' on the polymorphic object 'Entity#entity' without 'source_type'. Try adding 'source_type: "Entity"' to 'has_many :through' definition. 
    

    は最後に、ここで私が設定テストがあります。それらのオブジェクト間のデータを設定してアクセスできることを誰もが理解しているように、それらを与えます。私はFactoryGirlのモデルを詳しく説明しませんが、かなり明白です。

    require 'test_helper' 
    
    class UserEntityTest < ActiveSupport::TestCase 
    
        test "access entity from user" do 
        usr = FactoryGirl.create(:user_with_client) 
    
        assert_instance_of client, usr.user_entity.entity 
        assert_instance_of client, usr.entity 
        assert_instance_of client, usr.client 
        end 
    
        test "only right entity is set" do 
        usr = FactoryGirl.create(:user_with_client) 
    
        assert_instance_of client, usr.client 
        assert_nil usr.agency 
        end 
    
        test "add entity to user using the blind rails method" do 
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
    
        usr.build_user_entity(entity: client) 
        usr.save! 
    
        result = UserEntity.where(user_id: usr.id) 
        assert_equal 1, result.size 
        assert_equal client.id, result.first.entity_id 
        end 
    
        test "add entity to user using setter" do 
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
    
        usr.client = client 
        usr.save! 
    
        result = UserEntity.where(user_id: usr.id) 
        assert_equal 1, result.size 
        assert_equal client.id, result.first.entity_id 
        end 
    
        test "add entity to user using blind setter" do 
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
    
        usr.entity = client 
        usr.save! 
    
        result = UserEntity.where(user_id: usr.id) 
        assert_equal 1, result.size 
        assert_equal client.id, result.first.entity_id 
        end 
    
        test "add user to entity" do 
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
    
        client.users << usr 
    
        result = UserEntity.where(entity_id: client.id, entity_type: 'client') 
    
        assert_equal 1, result.size 
        assert_equal usr.id, result.first.user_id 
        end 
    
        test "only one entity by user" do 
    
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
        agency = FactoryGirl.create(:agency) 
    
        usr.agency = agency 
        usr.client = client 
        usr.save! 
    
        result = UserEntity.where(user_id: usr.id) 
        assert_equal 1, result.size 
        assert_equal client.id, result.first.entity_id 
    
        end 
    
        test "user uniqueness" do 
    
        usr = FactoryGirl.create(:user) 
        client = FactoryGirl.create(:client) 
        agency = FactoryGirl.create(:agency) 
    
        UserEntity.create!(user: usr, entity: client) 
    
        assert_raise(ActiveRecord::RecordInvalid) { 
         UserEntity.create!(user: usr, entity: agency) 
        } 
    
        end 
    
    end 
    

    私はこれが誰かの助けになることを願っています。私はMTIに比べて良いものが好きだと思うので、全体のソリューションをここに入れることにしました。

    +0

    @Crystark上記のファイルをテストすると、次のエラーが発生します。NameError:初期化されていない定数UserWithClient –

    0

    上記の答えは私にいくつかの問題をもたらしました。一意性を検証する場合は、モデル名の代わりに列名を使用します。 validates_uniqueness_of:userをvalidates_uniqueness_of:user_idに変更します。