DEV Community

Cover image for How to implement Laravel's whenLoaded function in Ruby on Rails
whchi
whchi

Posted on • Edited on

1

How to implement Laravel's whenLoaded function in Ruby on Rails

The ORM library in Laravel provides support for a conditional loading association method known as whenLoaded.

From the Laravel official document:

The whenLoaded method may be used to conditionally load a relationship. In order to avoid unnecessarily loading relationships, this method accepts the name of the relationship instead of the relationship itself.

Here's an example from Laravel's official document, use it with "ResourceResponse" can significantly enhance productivity.

use App\Http\Resources\PostResource;

/**
 * Transform the resource into an array.
 *
 * @return array<string, mixed>
 */
public function toArray(Request $request): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'posts' => PostResource::collection($this->whenLoaded('posts')),
        'created_at' => $this->created_at,
        'updated_at' => $this->updated_at,
    ];
}
Enter fullscreen mode Exit fullscreen mode

How to implement it in RoR?

To achieve what "ResourceResponse" does, we need the "grape" gem.

Given two entities, PostEntity and CommentEntity:

  • PostEntity.rb
module Entities
  class PostEntity < Grape::Entity
    expose :id, documentation: { type: 'Integer', desc: 'id' }
    expose :title, documentation: { type: 'String', desc: 'title' }
    expose :comments,
           using: CommentEntities,
           documentation: { is_array: true, desc: 'post comments' }
  end
end
Enter fullscreen mode Exit fullscreen mode
  • CommentEntity.rb
module Entities
  class CommentEntity < Grape::Entity
    expose :id, documentation: { type: 'Integer', desc: 'id' }
    expose :title, documentation: { type: 'String', desc: 'title' }
  end
end
Enter fullscreen mode Exit fullscreen mode

When we present PostEntity, we will receive a response with comments.

present Post.all with PostEntity # {id, title, comments: [{id, title}, {id, title}]} 
Enter fullscreen mode Exit fullscreen mode

However, if we don't want to retrieve comments with every single request, we can modify it as follows:

module Entities
# ...
    expose :comments,
           using: CommentEntities,
           documentation: { is_array: true, desc: 'post comments' },
           if: -> (instance, _options) { instance.association(:comments).loaded? }
end
Enter fullscreen mode Exit fullscreen mode

This way, comments will only be displayed if explicitly included:

# 
present Post.all with PostEntity # {id, title} 
present Post.includes(:comments).all with PostEntity # {id, title, comments: [{id, title}, {id, title}]} 
Enter fullscreen mode Exit fullscreen mode

bi-direction association

What if we want to load PostEntity from CommentEntity?

1. add an association to CommentEntity

module Entities
# ...
    # this will cause circular error
    expose :comments,
           using: CommentEntities,
           documentation: { is_array: true, desc: 'post comments' },
           if: -> (instance, _options) { instance.association(:post).loaded? }
end
Enter fullscreen mode Exit fullscreen mode

2. fix circular error

module Entities
# ...    
    expose :comments,
           using: CommentEntities,
           documentation: { is_array: true, desc: 'post comments' },
           if: -> (instance, options) {
             options[:parent] == instance.class.name && 
             instance.association(:post).loaded? 
           }
end
Enter fullscreen mode Exit fullscreen mode

3. present

present Comment.includes(:post).all with CommentEntity, parent: 'Comment' 
# {id, title, post: {id, title}} 

present Post.includes(:comments).all with PostEntity, parent: 'Post' 
 # {id, title, comments: [{id, title}, {id, title}]} 
Enter fullscreen mode Exit fullscreen mode

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay