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
Related
I'm making a simple API endpoint that returns an access code for an event.
If the event does not have access code, then it gets assigned one and saved in the database. Then, it checks if it's currently public or private. If private, return access code, if public, return empty string.
This is the endpoint controller:
public function getAc($eventId) {
// Pull event
$event = $this->eventService->api->getEventForce($eventId);
// If no access code for the event, generate one and update event record accordingly
if ($event->access_code == null) {
$access_code = $this->generateAccessCode();
DB::update('update events set access_code = ? where id = ?', [$access_code, $eventId]);
// Load updated event from DB.
$event = $this->eventService->api->getEventForce($eventId);
}
// Is the event currently private? return access code
if ($event->privacy=='private') {
return $event->access_code; // HERE: value comes back from the API but on MySQL Workbench it's still null.
}
// Is it public ? return empty string.
else {
return '';
}
}
My problem is that even though everything works as expected. When access_code is created it does come back from the api.
However when I check the record on MySQL Workbench (that connects to AWS Instance) it's still null! event though I pulled it from the database as a non-null value using the API endpoint.
Little confused with your code. From debugging I'd suggest checking your API for this issue. From what I can see you're doing this:
Ask API for event with ID 1
Check if event has a parameter
If no parameter, update using local DB
So I'm left asking, if the problem is with the API, why are you updating using the local instance of the DB? Furthermore could this be resolved using events? (I'm going to call your class something other than event so not to get confusing)
For instance:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Party extends Model {
public $table = 'parties';
public $fillable = [];
public static function boot() {
parent::boot();
static::creating(function($party) {
//create some fancy access code
$access_code = 'heyyyy';
//Check not manually set
if (!isset($party->attributes['access_code']) || is_null($party->attributes['access_code'])) {
$party->access_code = $access_code;
}
}
}
}
Now every time you create an event or 'party' using Party::create(); or $party = new Party; $party->save(); the creating event will pick up the save and also assign the access_code if you haven't set it manually like $party->access_code = 'you can\'t come';.
That's my thought anyway. However in your immediate case I think you need to ask yourself some more questions like:
Does the DB object from the instance of Laravel I'm using have access to the database to save said object?
Do I need to call the API in order to update my entity/model?
If the instance of Laravel I'm using from the \DB::update call have the same credentials as my API?
If this is a command or job, do my code changes affect it? Do I need to restart a supervisor/cron command to re-instance my code?
Who wrote this API? Is it reliable enough to use? And does it have documentation?
I'm fairly new to domain driven design concepts and I've run into a problem with returning proper responses in an API while using a command bus with commands and command handlers for the domain logic.
Let's say we’re building an application with a domain driven design approach. We have a back end and front end portion. The back end has all of our domain logic with an exposed API. The front end uses the API to make requests to the application.
We're building our domain logic with commands and command handlers mapped to a command bus. Under our Domain directory we have a command for creating a post resource called CreatePostCommand. It's mapped to its handler CreatePostCommandHandler via the command bus.
final class CreatePostCommand
{
private $title;
private $content;
public function __construct(string $title, string $content)
{
$this->title = $title;
$this->content= $content;
}
public function getTitle() : string
{
return $this->title;
}
public function getContent() : string
{
return $this->content;
}
}
final class CreatePostCommandHandler
{
private $postRepository;
public function __construct(PostRepository $postRepository)
{
$this->postRepository = $postRepository;
}
public function handle(Command $command)
{
$post = new Post($command->getTitle(), $command->getContent());
$this->postRepository->save($post);
}
}
In our API we have an endpoint for creating a post. This is routed the createPost method in a PostController under our Application directory.
final class PostController
{
private $commandBus;
public function __construct(CommandBus $commandBus)
{
$this->commandBus = $commandBus;
}
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->commandBus->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
}
Now in our createPost method we want to return the data of our newly created post in our response object so our front end application can know about the newly created resource. This is troublesome since we know that by definition the command bus should not return any data. So now we're stuck in a confusing position where we don't know how to add our new post to the response object.
I'm not sure how to proceed with this problem from here, several questions come to mind:
Is there an elegant way to return the post's data in the response?
Am I incorrectly implementing the Command/CommandHandler/CommandBus pattern?
Is this simply just the wrong use case for the Command/CommandHandler/CommandBus pattern?
First, notice that if we wire the controller directly to the command handler, we face a similar problem:
public function createPost($req, $resp)
{
$command = new CreatePostCommand($command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// How do we get the data of our newly created post to the response here?
return $resp;
}
The bus is introducing a layer of indirection, allowing you to decouple the controller from the event handler, but the problem you are running into is more fundamental.
I'm not sure how to proceed with this problem from here
TL;DR - tell the domain what identifiers to use, rather than asking the domain what identifier was used.
public function createPost($req, $resp)
{
// TADA
$command = new CreatePostCommand($req->getPostId()
, $command->getTitle(), $command->getContent());
$this->createPostCommandHandler->handle($command);
// happy path: redirect the client to the correct url
$this->redirectTo($resp, $postId)
}
In short, the client, rather than the domain model or the persistence layer, owns the responsibility of generating the id of the new entity. The application component can read the identifier in the command itself, and use that to coordinate the next state transition.
The application, in this implementation, is simply translating the message from the DTO representation to the domain representation.
An alternative implementation uses the command identifier, and derives from that command the identities that will be used
$command = new CreatePostCommand(
$this->createPostId($req->getMessageId())
, $command->getTitle(), $command->getContent());
Named UUIDs are a common choice in the latter case; they are deterministic, and have small collision probabilities.
Now, that answer is something of a cheat -- we've really only demonstrated that we don't need a result from the command handler in this case.
In general, we would prefer to have one; Post/Redirect/Get is a good idiom to use for updating the domain model, but when the client gets the resource, we want to make sure they are getting a version that includes the edits they just made.
If your reads and writes are using the same book of record, this isn't a problem -- whatever you read is always the most recent version available.
However, cqrs is a common architectural pattern in domain driven design, in which case the write model (handling the post) will redirect to the read model -- which is usually publishing stale data. So you may want to include a minimum version in the get request, so that the handler knows to refresh its stale cache.
Is there an elegant way to return the post's data in the response?
There's an example in the code sample you provided with your question:
public function createPost($req, $resp)
Think about it: $req is a representation of the http request message, which is roughly analogous to your command, and $resp is essentially a handle to a data structure that you can write your result into.
In other words, pass a callback or a result handle with your command, and let the command handler fill in the details.
Of course, that depends on your bus supporting callbacks; not guaranteed.
Another possibility, which doesn't require changing the signature of your command handler, is to arrange that the controller subscribes to events published by the command handler. You coordinate a correlation id between the command and the event, and use that to pull up the result event that you need.
The specifics don't matter very much -- the event generated when processing the command could be written to a message bus, or copied into a mailbox, or....
I am using this approach and I am returning command results. However, this is a solution which works only if the command handlers are part of the same process. Basically, I'm using a mediator, the controller and the command handler get an instance of it (usually as a constructor dependency).
Pseudo code controller
var cmd= new MyCommand();
var listener=mediator.GetListener(cmd.Id);
bus.Send(cmd);
//wait until we get a result or timeout
var result=listener.Wait();
return result;
Pseudo code command handler function
var result= new CommandResult();
add some data here
mediator.Add(result,cmd.Id);
That's how you get immediate feedback. However, this shouldn't be used to implement a business process.
Btw, this has nothing to do with DDD, it's basically a message driven CQS approach which can be and it is used in a DDD app.
I need help with caching Active Record data in Yii2.
In documentation has example:
$result = Customer::getDb()->cache(function ($db) {
return Customer::find()->where(['id' => 1])->one();
});
I understand that it need to implement the method in model class such a:
public static function getByPk($pk)
{
$result = self::getDb()->cache(function ($db) use ($pk) {
return self::find()->where(['id' => $pk])->one();
});
return $result;
}
If this query result was already cached in $result will set value from cache, otherwise will execute query, correctly?
Also I have question about dependency, what dependency I can use for invalidation this cache value?
As an opinion! You know it depends on data that you add to cache storage. If it's some static data such as status, position etc. you can set the second param duration. You can read about at official doc.
If you make a few same queries on the page you can set 1 or 2 seconds and it helps you a little.
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
What is considered the correct place for my pagination to live when using a service/datamapper/domain object trio?
Example:
Fetch ports with a few given criteria
Paginate the results
Know what page we are on, number of pages in total, number of results etc.. from the view class
The below is just something I wrote here and now, but it is similar to my application.
class PostController extends Controller
{
function viewPosts()
{
return $this->serviceFactory
->build('post')
->getPosts($aCriteria, $this->request->_get('page'), 10);
}
}
I am currently both calculating and storing pagination parameters in each service. Note that I am not pushing any data to the view from my controller.
class PostService extends AbstractService
{
public $posts;
public $iTotalPages; // This does not belong here does it?
function getPosts($aCriteria, $iPage, $iItemsPerPage)
{
$mapper = $this->dataMapperFactory->build('post');
// I do not know where to put the below
// (The parameters and the code itself)
$iCount = $mapper->count($aCriteria);
$iOffset = $iItemsPerPage * $iPage;
$this->iTotalPages = $iCount / $iItemsPerPage;
$this->posts = $mapper->find($aCriteria, $iOffset, $iOffset + $iItemsPerPage);
return $this->posts;
}
}
My views have access to the same instance of the Model layer as my controller, so I could call $service->iTotalPages from the view, but that feels wrong.
class PostsView extends AbstractView
{
function viewPosts()
{
$service = $this->serviceFactory->build('post');
if(count($service->posts)>0) {
$this->template->assign_var('TOTAL_PAGES', $service->iTotalPages);
$this->template->assign_vars('POSTS', $service->posts);
}
}
}
Solutions?
1) Create a service for pagination and have the controller exchange data with the post service as required?
2) Create a helper class for pagination that each service can include? (How would such a class look like?)
3) Add generic pagination to the AbstractService?
4) Add some sort of pagination support to my Repos?
You answer is pretty good, though I have a few suggestions.
Layer location:
The pagination infrastructure should live in the data access layer, this is so that you have low level control on how the data are retrieved.
Highest invocation
The pagination's interface should be abstracted and exposed in the UI through the service, since its a front-end concern. (I think you already have this covered in your answer)
The Abstraction:
What the UI should know are the page index, number of pages, number of items per page, and total items. Not the Offset nor the limit, these are infrastructure terms that should be encapsulated.
Input: (part of your fitlers)
Search filters
Sorting filters (if necessary)
Page index.
Number of items per page. (if the UI has control, if not then this should be encapsulated)
Output:
Filtered collection
Number of pages (for navigation)
Number of total items (if necessary in the UI)
Due to lack of answers to this question I am posting the solution I came up with. Appreciate comments/additions to it if it can be improved.
// Controller method
public function viewPosts()
{
// Create service objects
$postService = $this->serviceFactory->build('post', true);
$searchService = $this->serviceFactory->build('search');
// Set the search/filter parameters
$searchService->addFilter('author_id', $this->visitor->user_id);
$searchService->setOffset($this->request->_get('offset'));
$searchService->setLimit($this->request->_get('limit'));
// Search service will call the 'find' method on $articleService
$searchService->setServiceObject($articleService, 'find');
// Search service will return a filtered collection
return $searchService->search();
}
Doing it this way I am not leaking business logic into my controller (I think), and I have a single class to do all my filtering and analysis of the returned sql data, while still keeping each specific service in control of their specific find() method and datamappers.