Search code examples
sqlruby-on-railsrubymodel-view-controllercontroller

How can I call a create method of a controller in another controller in Ruby on Rails?


I have two controllers one is Payments and one is Transactions, I need to call the create method of transactions inside the create method of payments. So that each time I create a payment a transaction is automatically created. How should I approach this?

module Api
class PaymentsController < ApplicationController
  before_action :set_payment, only: %i[ show update destroy ]

  def index
    @payments = Payment.all
    render json: @payments
  end
  def show
    render json: @payment
  end
  def create
    @payment = Payment.new(payment_params)
    if @payment.save
      render json: 'Payment Is Made sucessfully'.to_json, status: :ok
    else
      render json: @payment.errors, status: :unprocessable_entity
    end
  end
  private
    def payment_params
      params.permit(:currency, :amount, :payment_type, :payment_address)
    end
end
end
module Api
class TransactionsController < ApplicationController
  before_action :set_transaction, only: %i[show update destroy]

  def index
    @transactions = Transaction.all
    render json: @transactions
  end
  def show
    render json: @transaction
  end
  # POST /transactions
  def create
    @transaction = Transaction.new(transaction_params)
    if @transaction.save
      render json: @transaction, status: :created, location: @transaction
    else
      render json: @transaction.errors,status::unprocessable_entity
    end
  end
  private
  def transaction_params
    params.permit(:transaction_type, :bank_name, :debit, :credit, :total_amount)
  end
end
end

Solution

  • First to note, MVC is just a convention or good way to organize your code.

    You can simply do:

    def create
      @transaction = Transaction.new(transaction_params)
    
      # You'll have to figure out how to get the params in here - maybe they are all derived from the transaction?
      @payment = Payment.new()
    
      @payment.save
    
      # handle payment error here too like below
    
      if @transaction.save
        render json: @transaction, status: :created, location: @transaction
      else
        render json: @transaction.errors,status::unprocessable_entity
      end
    end 
    

    However, this doesn't present itself well towards reusable code.

    We have 2 options - put it into the model or introduce a service.

    In the transaction model, we can create a function like:

    class Transaction
      ...
    
      def self.create_with_payment(params)
        Transaction.create(params)
        Payment.create(params)
    
        # do whatever here to create and associate these
      end
    

    or we can introduce a service object:

    class TransactionPaymentCreator
      def call(params)
        Transaction.create(params)
        Payment.create(params)
      end
    end
    

    You can then call this in your controller:

    def create
      service = TransactionPaymentCreator.new
      service.call
    end
    

    I'm leaving out a lot of detail like how to set these things up - but I hope to convey to you the general details - you have 3 options on how to make this work.

    Here is a good article and resource on reading more for service objects if you decide to go that route:

    https://www.honeybadger.io/blog/refactor-ruby-rails-service-object/