In a Symfony application you can use the EntityValueResolver to automatically fetch an entity when declaring the controller route.
How can one set a PESSIMISTIC_WRITE lock mode for this fetch?
This very basic controller successfully fetch the Product entity but it applies no lock.
#[Route('/product/{id}')]
public function show(#[MapEntity] Product $product): Response
{
// use the Product!
// ...
}
There is no option to define lock in EntityValueResolver as a function argument. You can do it with Request Object in the argument and apply the lock inside function.
$entity = $this->em->find(Product::class, $id);
// use the product for some read-only code
// Later, Need to update the product
$this->em->lock($entity, LockMode::PESSIMISTIC_WRITE);
$entity->setStock($entity->getStock() - 1);
$this->em->flush();
Related
I'm using Redis to cache different parts of my app. My goal is to not make a database query when the user is not logged in, as the app's content don't get updated regularly.
I cache the archive queries in my controller, however when I type hint a model in the controller, the model is retrieved from the database and then passed to the controller:
// My route
Route::get('page/{page:id}', [ PageController::class, 'show' ] );
// My controller
public function show ( Page $page ) {
// Here, the $page will be the actual page model.
// It's already been queried from the database.
}
What I'm trying to do is to try and resolve the page from the cache first, and then if the cache does not contain this item, query the database. If I drop the Page type-hint, I get the desired result ( only the id is passed to controller ) but then I will lose the benefit of IoC, automatic ModelNotFoundException, and more.
I've come across ideas such as binding the page model to a callback and then parsing the request(), but seems like a bad idea.
Is there any way to properly achieve this? I noticed that Laravel eloquent does not have a fetching event, which would be perfect for this purpose.
You can override the default model binding logic:
Models\Page.php
public function resolveRouteBinding($value, $field = null)
{
return \Cache::get(...) ?? $this->findOrFail($value);
}
Read more here https://laravel.com/docs/8.x/routing#customizing-the-resolution-logic
In order to check for existence of the data in Redis, you shouldn't type-hint the model into the controller's action. Do it like this:
public function show($pageId) {
if(/* check if cached */) {
// Read page from cache
} else {
Page::where('id', $pageId)->first();
}
}
I am evaluating the CQRS pattern and wonder what would be the best way to obtain an Entity created by a command in the same action so I can render it in the view.
The two options I can think of are.
1) Create an id in the controller and send it with the command to fetch the entity by finding it by id.
2) Create an instance of the entity and send it with the command so I have a reference to it after it's populated
Example code
public function createEntityAction(array $data) {
$eventDispatcher = $this->get('event_dispatcher');
$eventDispatcher->dispatch(
CreateEntityHandler::name, // Handler
new Entity($data) // Command
);
// Placeholder //
$entity = get-the-created-entity
// //
return $this->view($entity, Response::HTTP_OK);
}
Second option is not really an option. "Entity creation", which is in fact is a business operation, is a command handling.
Generally speaking, the one who sends a command, whose handler creates an entity, should send the entity id with it. In what way the identity is generated is just an implementation concern.
Usually, command handlers either do what they suppose to do and return nothing (or ACK) or throw (or NAK).
I have a table that tracks a customer's status as stored in a third party database. My table should only be updated when I can successfully update the other database via an API call.
When using Doctrine, is it a bad practice to add the API call into the setter method in my entity class? For example:
public function setCustomerStatus( $cusotmerStatus )
{
$api = new externalApi();
if( $api->updateStatus( $customerStatus ) )
{
$this->customerStatus = $customerStatus;
}
else
{
return 'Could not update customer status';
}
}
If you have an Entity field that can only be set under a certain condition, you have two options; either make the check before the update:
if($api->updateStatus($customerStatus){
$entity->setCustomerStatus($customerStatus);
}
Or, make the check within the Entity, such as you have done in the set method. The advantage of containing it within the set method is that you don't leave room for error; unfamiliar developers may not know to run the check prior to calling the set method, or you just may forget. Therefore, if you can guarantee the check need always be made, I prefer the option you have chosen
In my application i get product list through an api request. For listing the product in each and every page i need to perform the same api request each and every time with userid as parameter. This will increase the server load, so to avoid this i need to get product list and reuse the instance.
How can i implement this with a single call by introducing DI or Dependency Container.
Or is there any other technique to accomplish this ? i don't want to use session or database
The good approach is to use cache. For example:
// your service
public function getProducts($id)
{
if ($cache = $this->cache->hasItem($id)) {
return $this->cache->getItem($id);
}
// call api
$result = $this->api->call($id);
$this->cache->setItem($id, $result);
return $result;
}
For cache instance you can use Zend\Cache
I am writing a small e-shop application with Symfony 2 and I need some way to store the user's shopping cart in a session. I think using a database is not a good idea.
The application will use entities like Product, Category, ShoppingCart where Product and Category are persisted into the database and users will be choosing products into their ShoppingCart.
I have found NativeSessionStorage class which is supposed to save an entity into a session. But there is no written process of implementation into an application.
Do I use this in the controller or in a separated class ShoppingCart? Could you give me a short example of NativeSessionStorage usage?
EDIT:
The question was not set correctly:
The goal is not to save all product ids into a cookie. The goal is to save only a reference for basket (filled with products) in application memory on server-side and assign proper basket to user. Is this even possible to do this in PHP?
EDIT2:
Is a better solution to use a service?
Don't know if this way is the better way to store your data temporary. You can use this to instantiate a session object :
$session = $this->get("session");
Don't forget the 'use' in your controller :
use Symfony\Component\HttpFoundation\Session;
Then, the session starts automatically when you want to set a variable like :
$session->set("product name","computer");
This is based on the use of the Session class, easy to understand. Commonly definitions :
get(string $name, mixed $default = null)
Returns an attribute.
set(string $name, mixed $value)
Sets an attribute.
has(string $name)
Checks if an attribute is defined.
Also, take a look to the other ways to store your data : Multiple SessionStorage
You can make your entity Serializable and serialize the entity object and save to session and then retrieve in other page using unserialize(). There is one caveat, for an entity that exists in the db Doctrine2 will mark the retrieved/unserialized entity as detached. You have to call $em->merge($entity); in this case.
You can save the whole object into a session with Symfony. Just use (in a controller):
$this->get('session')->set('session_name', $object);
Beware: the object needs to be serializable. Otherwise, PHP crashes when loading the session on the start_session() function.
Just implement the \Serializable interface by adding serialize() and unserialize() method, like this:
public function serialize()
{
return serialize(
[
$this->property1,
$this->property2,
]
);
}
public function unserialize($serialized)
{
$data = unserialize($serialized);
list(
$this->property1,
$this->property2,
) = $data;
}
Source: http://blog.ikvasnica.com/entry/storing-objects-into-a-session-in-symfony (my blogpost on this topic)