MVC Design Question - dependent objects

Given

entity has_one, :client

client belongs_to :entity

When creating a new client and an instance of entity.name does not exist then I want to add an entity and a client. (This is done and works)

If entity.name exists then I want to check if entity.client also exists.

If entity.client exists then I want to return both the entity and client instances from the database to the user and ask if this is the client they wished to create.

If entity.client does not exist then I want to return the entity to the user and ask if this is the entity that they wish to add as a client and allow them to add the client role to that entity.

So, does the code for this sort of checking and shuffling of data belong mainly in the model or mainly in the controller? Are there Rails specific considerations that bear upon the answer?

The trend seems to be to keep as much logic on the Model as possible, and only use the controller for grabbing the data that the View needs. In this case, your controller needs to know if it should tell the view to ask a question. You should let the model take care of the how and why. It's not necessarily a Rails specific design pattern, more than it is considered best practice.

On a side note, I'm a fan of using the before_filter to do a little pre-processing before hitting the code in the action. For example in your Entity controller:

  before_filter :check_for_client, :only => :edit

  def edit     #stuff related to editing an entity   end

  private   def check_for_client      @entity = Entity.find(params[:id])      redirect_to(new_client_path) unless @entity.has_client?   end

In addition to performing you check, your entity record is already loaded for your edit action.

Bryan M. wrote:

On a side note, I'm a fan of using the before_filter to do a little pre-processing before hitting the code in the action. For example in your Entity controller:

  before_filter :check_for_client, :only => :edit

  def edit     #stuff related to editing an entity   end

  private   def check_for_client      @entity = Entity.find(params[:id])      redirect_to(new_client_path) unless @entity.has_client?   end

I just want to clarify something. When one references the entity through the client new action then this code must go into the clients_controller.rb correct? I understand that controllers are never implicitly invoked by reference to the model. Am I correct?

If this code is used in multiple controllers then is it best to extract it as a helper method or is there a different preferred way of doing that?

If this code is used in multiple controllers then is it best to

extract

it as a helper method or is there a different preferred way of doing that?

Helpers are really meant to help automate view components. If you want to share some common functionality to your controllers, I would put that code into a module, and save it in the "lib" folder. You can then include that in your controllers (or models, or views) by just adding "include MyModule" to the controllers you want it accessible to. If you think it should be accessible to all your controllers, you can add the include statement to your ApplicationController.

I would say only use a module if it's really necessary to share code. Typically, you'll want to keep your CRUD operations inside of the controller. You're right that models shouldn't invoke the controller, but I think you're forgetting that the Entity model and the Client model are already connected through their relationship.

In your Client Controller, you should have no trouble accessing it's Entity through @client.entity, and vice versa in the Entity Controller (@entity.client).

So from the previous example, if you go to edit an Entity with no client, it redirects to the Client's new action, where you ask a user if he'd like to add a client. Your client controller might look something like this:

  before_filter :load_entity

  def new     @client = Client.new   end

def create     @client = Client.new(params[:client])     @client.entity = @entity     @client.save!   end

  private   def load_entity     #since this is a has_one, we can load the entity in for the whole controller     @entity = Entity.find(params[:entity_id])   end

You'll have to be passing the :entity_id in from somewhere. You can do this in your re-directs:

redirect_to(new_client_path, :entity_id => @entity.id)

or if you're using nested routes:

redirect_to(new_entity_client_path(@entity))

Hope this helps.