Le message suivant est basé sur Rails 4.
Je recherche actuellement de bonnes pratiques sur les multiples ressources imbriquées (plus de 1) et l'option peu profonde: true.
D'abord dans mes itinéraires, il y avait ceci:
resources :projects do
resources :collections
end
Les itinéraires associés sont:
project_collections GET /projects/:project_id/collections(.:format) collections#index
POST /projects/:project_id/collections(.:format) collections#create
new_project_collection GET /projects/:project_id/collections/new(.:format) collections#new
edit_project_collection GET /projects/:project_id/collections/:id/edit(.:format) collections#edit
project_collection GET /projects/:project_id/collections/:id(.:format) collections#show
PATCH /projects/:project_id/collections/:id(.:format) collections#update
PUT /projects/:project_id/collections/:id(.:format) collections#update
DELETE /projects/:project_id/collections/:id(.:format) collections#destroy
projects GET /projects(.:format) projects#index
POST /projects(.:format) projects#create
new_project GET /projects/new(.:format) projects#new
edit_project GET /projects/:id/edit(.:format) projects#edit
project GET /projects/:id(.:format) projects#show
PATCH /projects/:id(.:format) projects#update
PUT /projects/:id(.:format) projects#update
DELETE /projects/:id(.:format) projects#destroy
J'ai lu dans la documentation sur la limitation des ressources imbriquées:
Les ressources ne doivent jamais être imbriquées à plus d'un niveau de profondeur.
Source: http://guides.rubyonrails.org/routing.html#limits-to-nesting Ok. Ensuite, comme le dit la documentation, je vais utiliser "peu profond" dans mes itinéraires.
shallow do
resources :projects do
resources :collections
end
end
Les itinéraires associés sont:
project_collections GET /projects/:project_id/collections(.:format) collections#index
POST /projects/:project_id/collections(.:format) collections#create
new_project_collection GET /projects/:project_id/collections/new(.:format) collections#new
edit_collection GET /collections/:id/edit(.:format) collections#edit
collection GET /collections/:id(.:format) collections#show
PATCH /collections/:id(.:format) collections#update
PUT /collections/:id(.:format) collections#update
DELETE /collections/:id(.:format) collections#destroy
projects GET /projects(.:format) projects#index
POST /projects(.:format) projects#create
new_project GET /projects/new(.:format) projects#new
edit_project GET /projects/:id/edit(.:format) projects#edit
project GET /projects/:id(.:format) projects#show
PATCH /projects/:id(.:format) projects#update
PUT /projects/:id(.:format) projects#update
DELETE /projects/:id(.:format) projects#destroy
La différence majeure que je vois est le "show" des collections, celui-ci:
collection GET /collections/:id(.:format) collections#show
Donc si j'ai raison, le lien pour l'action show pour une collection est:
<%= link_to 'Show", collection_path(collection)%>
et devrait renvoyer quelque chose comme ceci: " http://example.com/collections/1 "
MAIS ! 2 choses:
Je ne comprends pas quel est l'intérêt du superficiel si c'est pour perdre le gros avantage des actions de repos. Quel intérêt? Et quel est l'intérêt de perdre l'action "Show"? J'ai déjà posté ceci sur SO, mais le seul commentaire que j'ai reçu est "C'est quelque chose de normal". WTF? Dans quel cas s'agit-il d'un comportement normal pour "supprimer" une action de l'API de repos?
J'ai reproduit le problème sur un projet neutre, pour être sûr que je ne faisais pas quelque chose de mal, et le même problème s'est produit. Donc, oui, il peut être pratique pour les assistants d'utiliser peu profond, mais ce n'est PAS AT TOUS pratique pour le reste, vous perdez tout l'intérêt de "une collection est imbriquée dans un projet, donc cela se reflète dans l'URL ".
Je ne sais pas s'il y a une autre façon de le faire, c'est vrai que peu profond permet plus de flexibilité sur les assistants, mais c'est faux qu'il soit conforme au repos. Donc, y a-t-il une chance de faire fonctionner les "helpers" (c'est assez génial d'avoir "nested3_path (collection)" au lieu de "nested1_nested2_nested3 ([nested1.nested2.nested3, nested1.nested2, nested1])", et de garder le " partie url "et continuer à avoir" nested1/123/nested2/456/nested3/789?
Merci !
Je ne pense pas que Rails offre un moyen intégré pour que les URL utilisent la hiérarchie complète (par exemple /projects/1/collections/2
) mais également les raccourcis (par exemple collection_path
au lieu de project_collection_path
).
Si vous vouliez vraiment le faire, vous pouvez déployer votre propre assistant personnalisé comme suit:
def collection_path(collection)
# every collection record should have a reference to its parent project
project_collection_path(collection.project, collection)
end
Mais ce serait assez lourd à faire manuellement pour chaque ressource.
Je pense que l'idée derrière l'utilisation des routes shallow
est mieux résumée par la documentation:
Une façon d'éviter l'imbrication profonde (comme recommandé ci-dessus) consiste à générer les actions de collecte définies sous le parent, afin d'avoir une idée de la hiérarchie, mais de ne pas imbriquer les actions membres. En d'autres termes, pour créer uniquement des itinéraires avec le minimum d'informations pour identifier de manière unique la ressource
source: http://guides.rubyonrails.org/routing.html#shallow-nesting
Ainsi, bien que cela ne soit pas conforme à REST (comme vous le dites), vous ne perdez aucune information car chaque ressource peut être identifiée de manière unique et vous pouvez remonter la hiérarchie en supposant que vos associations sont correctement configurées.
Puisqu'il existe un id
pour un Collection
, il est redondant d'imbriquer la route sous le projet à l'exception des actions index
et create
.
Il y a une règle sur les URL où il n'y a qu'une seule URL pour GET (avec 200) une ressource donnée, s'il y a d'autres URL, vous devez les rediriger. Vous pourriez donc avoir un itinéraire /projects/:id/collections/:collection_id
qui redirige vers /collections/:collection_id
.
Dans votre cas, une collection est liée à un projet, mais ce n'est pas nécessairement vrai pour toutes les relations. Une fois que vous avez le :collection_id
vous n'avez pas besoin de référencer le contexte de Project
pour y accéder.
Niveaux
La notion selon laquelle vous ne devez utiliser qu'un seul niveau dans vos ressources imbriquées n'est vraiment applicable qu'à la conception du système:
L'assistant d'itinéraire correspondant serait publisher_magazine_photo_url, vous obligeant à spécifier des objets aux trois niveaux. En effet, cette situation est assez déroutante pour qu'un article populaire de Jamis Buck propose une règle d'or pour de bon Rails design:
Je crois que Rails peut toujours gérer plusieurs niveaux, bien que ce ne soit pas recommandé du point de vue de l'utilisabilité
Peu profond
Bien que j'aie déjà vu de l'eau peu profonde auparavant, je ne l'ai jamais utilisée moi-même
En regardant le documentation , il semble peu profond a un but plutôt obscur (je ne sais pas vraiment pourquoi il est là). Le problème est que vous ne passez pas publiquement le post_id
paramètre à votre contrôleur, vous laissant charger le collection
sans paramètre important
Je suppose (et ce ne sont que des spéculations) que l'objectif est de passer les paramètres dont vous avez besoin dans les coulisses, de sorte que vous vous retrouvez avec un itinéraire public "peu profond":
#config/routes.rb
resources :projects do
resources :collections, shallow: true
end
J'imagine que vous obtiendrez un assistant URL comme celui-ci:
collection_path(project.id, collection.id)
Cela sortirait comme domain.com/collection/2
Bien que cela puisse compliquer les choses si vous n'en avez besoin que pour certains modèles, il peut être utile de vérifier Ressources héritées (IR). Il prend en charge l'imbrication des ressources, appartient à polymorphe et peut générer automatiquement les méthodes d'assistance de chemin d'accès et d'URL plus courtes que vous recherchez. La raison pour laquelle vous n'entendez plus beaucoup parler d'IR est que son auteur d'origine et certains autres développeurs l'ont quelque peu abandonné en raison des complications qui surviennent lorsque vous essayez d'étendre vos contrôleurs. Cependant, il a toujours une communauté, et nous avons essayé de l'étendre un peu plus et de nous concentrer davantage sur la facilité des extensions de contrôleur avec Irie .
La "meilleure pratique" dans Rails dépend de qui vous parlez.
Rails a traditionnellement visé principalement des CRUD de base pour les ressources (non imbriquées). Oui, il permet de récupérer et de mettre à jour les ressources imbriquées, mais on suppose que cela ne se produit pas aussi souvent.
Cependant, ce qui a émergé dans la communauté Rails est l’approche ActiveModel :: Serializers / json-api . Dans ce cas, généralement pas plusieurs niveaux d'imbrication des ressources se produisent, et la ressource imbriquée est soit une liste de liens, soit une petite version des ressources enfants, que vous pouvez ensuite interroger sur cette ressource pour obtenir plus de données. Cela a également été adopté par Ember / Ember Data .
Il y a aussi rugissement et un certain nombre d'autres projets qui visent à mettre en œuvre quelque chose de plus proche de leur compréhension de quelque chose de proche de la vision originale de Roy Fielding de REST.
Je pense que cela dépend simplement de votre conception et de vos besoins. Si l'efficacité est un objectif, alors le temps supplémentaire pour développer pour être explicite et imbriquer plus peut être payant. Nous utilisons actuellement AngularJS et Irie , par exemple. Mais a chacun le sien.
Comme dernière remarque, assurez-vous d'éviter les recherches n + 1 en utilisant includes(...)
(ou similaire) dans vos requêtes, sinon tout l'imbrication pourrait vous nuire aux performances.
D'après cette réponse il semble que les routes peu profondes défient quelque peu la convention de Rails, OMI.
Je pense que vous n'auriez pas besoin de l'aide de chemin explicite pour un itinéraire de spectacle. L'assistant link_to doit pouvoir le déduire de la méthode to_param de l'objet.
#your helper becomes
link_to "show", collection
Si vous utilisez l'aide comme vous le faites ci-dessus, vous devrez probablement également transmettre l'ID imbriqué de la ressource parent à l'aide.
link_to "show", collection_path([project, collection])