/ Tags: RAILS-CODE / Categories: SOLUTIONS

Serialize Query Params Back Into A Url String In Rails

Filter forms, pagination links, and sort toggles all need to rebuild the current URL with one parameter changed and the rest preserved. Doing it with string concatenation breaks on the first array parameter or special character.

Description

to_query on a hash produces a properly encoded, alphabetically sorted query string, handling nested hashes and arrays with the bracket notation Rails parses on the way back in. In a view, url_for(params.permit(...).merge(...)) is usually better β€” it keeps the current controller and action and only rewrites the parameters you name. Always permit before merging. Passing raw params to url_for raises, and blindly permitting everything lets a crafted link inject parameters into every generated URL on the page.

Sample input:

  { status: "pending", tags: ["urgent", "new"], page: 2 }.to_query


Sample Output:

  page=2&status=pending&tags%5B%5D=urgent&tags%5B%5D=new

Answer

  # Hash to query string
  { status: "pending", page: 2 }.to_query
  # => "page=2&status=pending"

  # Arrays and nesting are handled
  { tags: ["urgent", "new"], filter: { min: 10 } }.to_query
  # => "filter%5Bmin%5D=10&tags%5B%5D=urgent&tags%5B%5D=new"

  # Parse it back
  Rack::Utils.parse_nested_query("tags[]=urgent&tags[]=new")
  # => { "tags" => ["urgent", "new"] }

  # In a controller or helper β€” keep current params, change one
  class ApplicationController < ActionController::Base
    PERMITTED_FILTERS = [:status, :sort, :direction, :page, :q, tags: []].freeze

    helper_method :url_with_params

    def url_with_params(overrides = {})
      url_for(params.permit(*PERMITTED_FILTERS).merge(overrides).to_h)
    end
  end
  <%= link_to "Next", url_with_params(page: @page + 1) %>
  <%= link_to "Sort by date", url_with_params(sort: "created_at", direction: "desc") %>
  <%= link_to "Clear status", url_with_params(status: nil) %>

Learn More

cdrrazan

Rajan Bhattarai

Full Stack Software Developer! πŸ’» 🏑 Grad. Student, MCS. πŸŽ“ Class of '23. GitKraken Ambassador πŸ‡³πŸ‡΅ 2021/22. Works with Ruby / Rails. Photography when no coding. Also tweets a lot at TW / @cdrrazan!

Read More