Search code examples
ruby-on-railsrubynullundefinednomethoderror

nil when trying to display data in rails app


I get this error when trying to show <%= @schedule.title %>, but when I do the same with <%= @lesson.title %> it is working fine for @lesson.

![undefined method `title' for nil:NilClass ]1

The flow is like this.

- A user signs up and creates a clinic, the clinic belongs to the user.
- A clinic has many practitioners and they belongs to the clinic.
- A practitioner has many lessons and schedules, and they belongs to the practitioner.

When I'm on the lesson show page, there is a link to a booking page. It's on this booking page that the error occours.

I know it's saying nil, but both lessons and schedules have been created for that specific practitioner.

Anybody knows why this is happening? I don't understand why I can access @lesson but not @schedule. Any help would be much appreciated.

routes.rb

  resources :clinics do 
    resources :practitioners do 
      resources :lessons, :lesson_payments, :schedules do
        resources :bookings do 
        end
      end
    end
  end

schedules_controller.rb

class SchedulesController < ApplicationController
  before_action :set_schedule, only: [:show, :edit, :update, :destroy]

  # GET /schedules
  # GET /schedules.json
  def index
    @schedules = Schedule.all
  end

  # GET /schedules/1
  # GET /schedules/1.json
  def show
  end

  # GET /schedules/new
  def new
    @schedule = Schedule.new
  end

  # GET /schedules/1/edit
  def edit
  end

  # POST /schedules
  # POST /schedules.json
  def create
    @schedule = Schedule.new(schedule_params)

    respond_to do |format|
      if @schedule.save
        format.html { redirect_to clinic_practitioner_schedule_path(id: @schedule.id), notice: 'Schedule was successfully created.' }
        format.json { render :show, status: :created, location: @schedule }
      else
        format.html { render :new }
        format.json { render json: @schedule.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /schedules/1
  # PATCH/PUT /schedules/1.json
  def update
    respond_to do |format|
      if @schedule.update(schedule_params)
        format.html { redirect_to clinic_practitioner_schedule_path(@schedule), notice: 'Schedule was successfully updated.' }
        format.json { render :show, status: :ok, location: @schedule }
      else
        format.html { render :edit }
        format.json { render json: @schedule.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /schedules/1
  # DELETE /schedules/1.json
  def destroy
    @schedule.destroy
    respond_to do |format|
      format.html { redirect_to clinic_practitioner_schedules_url, notice: 'Schedule was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_schedule
      @schedule = Schedule.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def schedule_params
      params.require(:schedule).permit(:title, :start, :end, :practitioner_id, :account_id)
    end
end

bookings_controller.rb

class BookingsController < ApplicationController

  before_action :set_lesson, only: [:new]

  def new
      @account = Account.new
      @user = User.new 
      @patient = Patient.new
      @booking = Booking.new
      @lesson_payment = LessonPayment.new
      @schedule = Schedule.find_by_id(params[:id])
  end

  def create
      create_patient_charge
      create_patient_account  
      @user = User.new(user_params)
      @user.account_id = @account.id

      respond_to do |format|
        if @user.save
          create_patient_profile
          create_patient_booking
          create_patient_lesson_payment
          auto_login(@user)
          UserMailer.new_signup_booking_admin(@user, @booking).deliver_later
          UserMailer.new_signup_booking_client(@user, @booking).deliver_later
          format.html { redirect_to dashboard_url, notice: 'Your account was successfully created.' }
          format.json { render :show, status: :created, location: @user }
        else
          format.html { redirect_back fallback_location: root_path, alert: 'An error occurred while sending this request.' }
          format.json { render json: @user.errors, status: :unprocessable_entity }
        end
      end
    end

  private

  def set_lesson
    @lesson = Lesson.find(params[:lesson_id])
  end


  def user_params
       params.require(:user).permit(:email, :password, :time_zone)
      end

      def create_patient_account
        @account = Account.new()
        @account.status = 'active'
        @account.account = 'prefix-' + SecureRandom.hex(4)
        @account.account_type = 'client'
        @account.save
      end
  def create_patient_profile
        @patient = Patient.new()
        @patient.firstname = params[:user][:patient][:patient_first_name]
        @patient.lastname = params[:user][:patient][:patient_last_name]
        @patient.phone = params[:user][:patient][:patient_phone]
        @patient.user_id = @user.id
        @patient.account_id = @user.account_id
        @patient.save
      end

      def create_patient_booking
        @lesson = Lesson.find(params[:user][:booking][:lesson_id])
        @booking = Booking.new()
        @booking.lesson_id = params[:user][:booking][:lesson_id]
        @booking.schedule_id = params[:user][:booking][:schedule_id]
        @booking.patient_id = @patient.id
        @booking.account_id = @user.account_id
        @booking.title = @lesson.title
        @booking.cost = @lesson.cost
        @booking.status = 'Booked'
        @booking.save
        @schedule = Schedule.find(params[:user][:booking][:schedule_id])
        @booking.practitioner_id = @schedule.practitioner_id
        @booking.start = @schedule.start
        @booking.refunded = 0
        @booking.save
        @schedule.title = 'Booked'
        @schedule.save
      end
     def create_patient_lesson_payment
        @lesson_payment = LessonPayment.new()
        @lesson_payment.status = 'Paid'
        @lesson_payment.date = Date.today
        @lesson_payment.cost = @lesson.cost
        @lesson_payment.service = @lesson.title
        @lesson_payment.booking_id = @booking.id
        @lesson_payment.account_id = @user.account_id
        @lesson_payment.save
      end

  end

This is where I link to the booking page
show.html.erb

<p id="notice"><%= notice %></p>
<%= link_to new_clinic_practitioner_lesson_booking_path(:lesson_id => @lesson.id), class: "course-btn" do %><i class="fa fa-calendar-plus-o"></i> Book This Lesson<% end %>
<p>
  <strong>Image:</strong>
  <%= @lesson.image %>
</p>

<p>
  <strong>Title:</strong>
  <%= @lesson.title %>
</p>

<p>
  <strong>Duration:</strong>
  <%= @lesson.duration %>
</p>

<p>
  <strong>Cost:</strong>
  <%= @lesson.cost %>
</p>

<p>
  <strong>Category:</strong>
  <%= @lesson.category %>
</p>

<p>
  <strong>Language:</strong>
  <%= @lesson.language %>
</p>

<p>
  <strong>Level:</strong>
  <%= @lesson.level %>
</p>

<p>
  <strong>Description:</strong>
  <%= @lesson.description %>
</p>
<p>
  <strong>Practitioner:</strong>
  <%= @lesson.practitioner_id %>
</p>
<p>
  <strong>Account:</strong>
  <%= @lesson.account_id %>
</p>

<%= link_to 'Edit', edit_clinic_practitioner_lesson_path(@lesson) %> |
<%= link_to 'Back', clinic_practitioner_lessons_path %>

new.html.erb

<h1>New Booking</h1>

<%= render 'form', booking: @booking %>

<%= link_to 'Back', clinic_practitioner_lesson_bookings_path %>

_form.html.erb

<table class="table table-bordered">
  <thead>
    <tr>
      <th>Slots In The User's Time Zone</th>
      <th>Price</th>
      <th>Service Provider</th>
      <th>Booking Button</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><%= @lesson.title %></td>
      <td><%= @schedule.title %></td>
    </tr>
  </tbody>
</table>

Solution

  • I am noticing that in you are initializing @schedule like this in your bookings_controller new method:

    @schedule = Schedule.find_by_id(params[:id])
    

    But, you don't have any params[:id] in your parameters that are:

    {'clinic_id'=>'50','practitioner_id'=>'27','lesson_id'=>'15'}
    

    This is why your @schedule is nil

    Assuming, there is a has_many association between practitioner and schedule, and you want to display the title of first schedule of the practitioner, you can do it it like this:

    @schedule = Schedule.find_by_practitioner_id(params[:practitioner_id])