ruby-on-railsalchemy-cms

how to set custom controller for page_layout


I'm trying to set my own controller for a page_layout, because I want to override the actions to get other models into the view.

Edit: frontend rendering works now. Only backend is buggy

From the docs, i did:

# config/alchemy/page_layouts.yml
- name: index
  unique: true
  elements: [article, other_element]
  autogenerate: [article]
  controller: 'my_custom'
  action: 'index'

and created a app/controllers/my_custom_controller.rb:

# app/controllers/my_custom_controller.rb
class MyCustomController < Alchemy::PagesController
  def index
    @some_values = Value.all
  end
end

my routes.rb lookes like this:

Rails.application.routes.draw do

  root 'my_custom#index'

  devise_for :users,
             path_names: { sign_in: 'login', sign_out: 'logout' },
             controllers: { sessions: 'users/sessions', passwords: 'users/passwords' }

  mount Alchemy::Engine => '/'

The root-page has the page_type index in alchemy.

And when i go to localhost:3000 I get what I want

But in the alchemy backend, when editing the page, i get an error when rendering:

NoMethodError in Alchemy::Admin::Pages#show
Showing app/views/alchemy/elements/_some_element_view.html.erb where line #2 raised:

undefined method `each' for nil:NilClass
Extracted source (around line #2):
<div class="row">
  <% for @value in @some_values %>
      <%= render 'value/basic_view' %>
    <% end %>
</div>

probably, because the @some_values are not initialized, what would mean, that my controller wasn't used to render this. Am I doing something wrong?

Edit2: Maybe I'm on the complete wrong track. I want to have an Alchemy-Element, that renders stuff from the database (custom models). What I did is, creating an element, that renders the @some_values. And by specifing my custom controller on every page-type where this element can be placed on, I allow my editors to place those @some_values where they want.


Solution

  • It looks like this is not the right approach for what you want. Custom controllers for page layouts was introduced for routing reasons. So that editors can manage the navigation for custom controllers. They are not meant for decorating the default element and page rendering.

    What you probably want is a on_page_layout callback: http://www.rubydoc.info/github/AlchemyCMS/alchemy_cms/Alchemy/OnPageLayout

    In your case:

    class ApplicationController < ActionController::Base
      extend Alchemy::OnPageLayout
    
      on_page_layout :foo do
        @some_values = Value.all
      end
    end
    

    Or, even easier, load the values inside the the element view. A little PHPish, but also ok.

    EDIT:

    From a performance perspective using low level rails cache in your controller is a good practice:

    ...
    on_page_layout :foo do
      @some_values = Rails.cache.fetch('some/cache/key', expires_in: 10.minutes) do
        Value.all
      end
    end
    ...