Je suis un développeur PHP apprenant la beauté de Ruby on Rails, j'aime ActiveRecord et j'ai remarqué quelque chose de vraiment intéressant, c'est ainsi que les méthodes ActiveRecord détectent la fin de la chaîne de méthodes pour exécuter la requête.
@person = Person.where(name: 'Jason').where(age: 26)
# In my humble imagination I'd think that each where() executes a database query
# But in reality, it doesn't until the last method in the chain
Comment fonctionne cette sorcellerie?
La méthode where
renvoie un ActiveRecord::Relation
objet, et par lui-même cet objet n'émet pas de requête de base de données. C'est où vous utilisez cet objet qui compte.
Dans la console, vous faites probablement ceci:
@person = Person.where(name: "Jason")
Et puis blammo il émet une requête de base de données et retourne ce qui semble être un tableau de tout le monde nommé Jason. Oui, Active Record!
Mais alors vous faites quelque chose comme ça:
@person = Person.where(name: "Jason").where(age: 26)
Et puis cela émet une autre requête, mais celle-ci s'adresse aux personnes qui s'appellent Jason qui ont 26 ans. Mais elle émet uniquement une requête ne, alors où est passée l'autre requête?
Comme d'autres l'ont suggéré, cela se produit car la méthode where
renvoie un objet proxy. Il n'exécute pas réellement une requête et ne renvoie pas un ensemble de données à moins qu'on ne lui demande de le faire.
Lorsque vous exécutez n'importe quoi dans la console, cela produira la version inspectée du résultat de tout ce que vous avez exécuté. Si vous mettez 1
dans la console et appuyez sur Entrée, vous obtiendrez 1
retour parce que 1.inspect
est 1
. La magie! De même pour "1"
. Une variété d'autres objets n'ont pas de méthode inspect
définie et donc Ruby revient à celui de Object
qui retourne quelque chose effroyable comme <Object#23adbf42560>
.
Chacun ActiveRecord::Relation
l'objet a la méthode inspect
définie dessus afin qu'il provoque une requête. Lorsque vous écrivez la requête dans votre console, IRB appellera inspect
sur la valeur de retour de cette requête et produira quelque chose de presque lisible par l'homme, comme le tableau que vous verriez.
Si vous veniez de publier ceci dans un script Ruby script standard, aucune requête ne serait exécutée tant que l'objet n'aurait pas été inspecté (via inspect
) ou a été itéré via each
, ou avait le to_a
méthode appelée.
Jusqu'à ce que l'une de ces trois choses se produise, vous pouvez enchaîner autant d'instructions where
que vous le souhaitez, puis lorsque vous faites appelez inspect
, to_a
ou each
dessus, il exécutera finalement cette requête.
Il existe un certain nombre de méthodes connues sous le nom de "kickers" qui déclenchent la requête vers la base de données. Avant cela, ils créent simplement AST, qui une fois lancés, généreront le SQL réel (ou le langage en cours de compilation) et exécuter la requête.
Voir ce billet de blog pour une explication plus approfondie de la façon dont cela est fait.
Vous pouvez lire le code, mais un concept ici est le modèle de proxy.
@Person n'est probablement pas le véritable objet mais un proxy de cet objet et lorsque vous avez besoin d'un attribut, l'enregistrement actif exécute finalement la requête. Hibernate a le même concept.