-
Notifications
You must be signed in to change notification settings - Fork 11.6k
[12.x] Introduce Contextable
#57325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 12.x
Are you sure you want to change the base?
[12.x] Introduce Contextable
#57325
Conversation
Alternatively, this could be an attribute |
Hey @cosmastech - am I missing something in your example where you actually add it to the context? |
Holy cow, yeah I did forget that. But we could automatically store it in after resolving Edit: updated the example |
interface Contextable
{
public function withContext($context)
{
if ($context->has('foo')) {
return;
}
$context->add('foo', $this->foo);
}
}
On the other side, if you wanted the data to continue to be dynamic, you can always use the I do note that this approach only works for objects you control. For objects you don't own, I suppose there is also a hooks style approach we could take, e.g., public function show(Model $model)
{
Context::whenRetreiving(fn () => Context::add('foo', $model->foo));
} |
FWIW, my initial implementation of Context supported dynamic data via Closures. I got it really close but then ended up stripping it all out as I ran into issues that were blocking my shipping the thing and were not worth the time, at the time. All that is to say, I like the idea! |
What concerns do you have? Just the cognitive overhead? Do you think it's ugly, or truly something that makes it harder for developers to use? I look at Context's public data as being something I want written to my logs, and the hidden data being something I want to keep accessible from multiple places in my app without having to create a data container class. We could possible just modify
I think passing the Repository instance as an argument makes sense. I am not personally a fan of having the Contextable get/set its own values into the Context though. This feels like it makes this significantly less user-friendly. Perhaps if the Were you proposing a second method on the class, or
For the use-case I had mentioned in the original post, I think that this unfortunately saps a lot of the usefulness out of this functionality. To me, it feels like restoring the object to its original state would be easiest to use. (This current implementation doesn't account for re-binding it as a singleton in the container though... which was how we were using it). In the project, we were storing manipulating file uploads via a dispatched job. The dispatched job would produce a PDF and store some metadata about it (like which app/user created it, on multiple tables). Otherwise, we have to fetch those values out of the Context's data, which leads to magic strings and untyped properties.
Yeah, that's what we had done in the project I was referencing. It's not particularly clean.
I'm not sure I can picture this use-case very well of an object you don't own being something you'd want to keep in Context? Could you provide me with a for instance here? |
I asked some questions above that I would love to get your thoughts on. As always @timacdonald, I really appreciate your feedback and encouragement. 🙇 |
Short summary
This is a simply way to create an object that syncs with the logging output.
Why? Dear god, why?
In a project I worked on semi-recently, we needed a "RequestContext" state that we stored. It was a class that was registered as a singleton in the container. A middleware would store the request data in the database, and then it would also keep a few other bits of data. Every time we updated the object via setters, it would re-sync itself to the Context facade.
In order to keep audit tracking, values from the request context was used in a number of inserts, hence why it was so meaningful to have it be a singleton.
Ok... maybe, but how do I use it.
It's pretty easy, just make yourself a Contextable object.
(Note: I fully expect Taylor to hate the idea of using an interface here, and instead we could just check that a method called
contextData()
exists on the Contextable. No problem)