Clean Architecture - How to manipulate a List? In Presenter? Or UseCase? - solid-principles

I have in my domain layer one entity called Product.
class Product{
String barcode;
String name;
String barcode;
double price;
int amount;
}
My UI has a Product list and when I touch the barcode scanner button, the camera read a barcode and increment the Product in UI Product List. If there's no Product in List with the same barcode, the app will add this new Product, but if there's some Product with the same barcode, the app will increment the amount of this Product.
In which layer should I add this rule? Should I do this verification in UseCase? Or in Presenter?
Because I could have 2 UseCases:
The first one will expect a barcode, and will return a Product with the same barcode.
The second will expect a Product, and will increment the Product amount and return itself.
And I could use the Presenter to verify if in list has some product with the same barcode, if there's, the Presenter will call the second UseCase to increment the Product amount. Else, the Presenter will call the first UseCase to return a new Product and then, the Presenter will add to List.
Or Should I do this all verification in UseCase? I mean, the Presenter always will call just a UseCase that increment the Product and expect all the List and return all the List.
So my UseCase will verify the List passed as a parameter, if the Product exists in list, the UseCase will increment and return the List, if the Product doesn't exists, the UseCase will return the List with the new Product.
I know that UseCase should have all the business rule, and this rule "add a product if it doesn't exists or increment it amount if it does" its like a business rule. But I don't know a way to make the UseCase use this rule.

It seems that you have 2 use cases:
addScannedProduct
The addScannedProduct use case needs access to a repository that returns a product by a bar code.
addProduct
The addProduct use case retrieves a product by a product id from the repository.
Usually you will also have 2 controllers that handle the different user interactions.
One controller that listens to the scanner result or get triggered by the scanner. This controller invokes the addScannedProduct use case.
One controller that listens or is triggered by a butten press or any other kind of user interaction. This controller invokes the addProduct use case.
Consolidate the product list in the presenter or not?
The question is "Is the rule UI dependent or UI agnostic.". So Ask yourself "Should a product list in another UI also be consolidated or not".
I guess that the product list should be consilidated independent of the presentation. Thus I would put that consolidation in the use case.

Related

Laravel: How to get counter value when inserting with UUID and Auto Increment

My models have both id and counter attributes. The id is a UUID, and the counter is an integer which is auto-incremented by the database.
Both are unique however I rely on id as the primary key. The counter is just a human-friendly name that I sometimes display to the user.
Immediately before an object is created a listener gives it a UUID. This works fine.
When the record is saved, MySQL increments the counter field. This works fine except that the copy of the object which I have in memory does not have the counter value. I can reload the object to find out what its counter is, but that would require another database query.
Is there a way to find the value of the counter without a specific database query? For example, is it returned as part of the response from the database when a record is created?
Few things:
Use create(array $attributes) and you'll get exactly what you want. For this having right, you have to ensure that $fillable array consists all attributes' names passed to create method.
You should use Observer on model instead of listener (most likely creating method).
Personal preference using Eloquent is that you should use id for id (increment field) and forget custom settings between models because by default it is what relations expect and so on
public function secondModels()
{
return $this->hasMany(SecondModel::class);
}
is pretty much no brainer. But for having this working best way would be (also following recommendations of this guy) FirstModel::id, SecondModel::id, SecondModel::first_model_id; first_models, second_models as table names. Avoiding and/or skipping this kind of unification is lot of custom job afterward. I don't say it can't be done but it is lot of non-first-time-successful work done.
Also, if you want visitor to get something other than id field name, you can make computed field with accessor:
/**
* Get the user's counter.
*
* #return string
*/
public function getCounterAttribute(): string
{
return (string)$this->id;
}
Which you call then with $user->counter.
Also personal preference of mine is to have most possible descriptive variable names so uuid field of mine would be something like
$table->uuid('uuid4');
This is some good and easy to make practice of Eloquent use.
Saying all this let me just to say that create() and save() will return created object from database while insert() shall not do it.

how to track changes of relation between two models in yii2

I have two models with a simple 1-n relation (category and item with category_id). I would like to show how much items there is in the category. I would like to have the number cached rather then always doing the count, so I have an extra field in category table called "total_items_count".
How to best do this total count triggering, when to call the "countUpdate" function since the relation could change from several places (backend, api, frontend...).
My initial plan was to use AFTER_UPDATE event, but "link()" must be called after the item is stored in database (on adding new item at least) so then I do not know which category is the item related to. I also need to know the old category, in case item goes from one category to another. In the backend controller I am using $item->link('category', $categoryObj); as I might change this relation to n-n someday.
Any advice on how to have complete control if the link between item and category changes and then update the count for the old and new linked category?
Thanks
The first and most important thing you need to now, is that you need to update the category count field via the ActiveRecord::updateCounters() method, in order to prevent incorrect update from multiple places. So you can do one of those things:
In the category item model you can do the following:
public function save($runValidation = true, $attributeNames = null)
{
if (parent::save(runValidation, attributeNames)) {
$this->category->updateCounters(['total_items_count' => 1]);
return true;
}
return false;
}
Or you can write a centralized component (let's say app\components\Category) which you will call from the whole application. This component will have a method that saves an item. For example:
public function saveItem($category, $item)
{
if ($item->save()) {
$item->link('category', $category);
$category->updateCounters(['total_items_count' => 1]);
return true;
}
return false;
}
Of course, when you delete an item, you will call:
$category->updateCounters(['total_items_count' => -1]);
See the ActiveRecord::updateCounters() API
From the Yii2 documentation:
Note: If you use yii\db\ActiveRecord::save() to update a counter column, you may end up with inaccurate result, because it is likely the same counter is being saved by multiple requests which read and write the same counter value.
If you need to keep track of which is the old category, just create another column let's say "old_category" to the category item table and when you change the category of the item put the old category id there, and the new category id to the category_id column.
Does that helps? If you have any questions, please ask!

Using Doctrine2 with Foreign Keys in Symfony2

I have a MySQL database (that is later imported into Doctrine) set up that links 3 different tables by way of foreign keys. The relationship is as follows: Many As go to one B, and many Bs go to one C. For the web page that I am trying to create, I need some of the related B's information on a page about A, while being categorized by C.
Think of it like this: A is "dog_food", B is "company", and C is "company_category". That is, on a page displaying different kinds of dog food, I need to display information about the manufacturing company; I only display those dog foods based on how the user specifies what kind of company they want to buy from.
Pulling information dynamically from A is trivial, since the repository is pulled and the rows exist as entities. Say, {{ dog_food.price }} would call (if specified in a for loop, this is Twig code) a single instance's price.
I have read about the #OneToOne, #OneToMany, etc. annotations, but have not been able to find a way to easily utilize their effects inside of a Twig template. The aggregate entities and repositories for all 3 tables exist as variables in the Controller. It should be mentioned that, continuing with this example, that there is a single companyID field in table B corresponding to multiple dog foods, and a single categoryID associated with multiple companies.
What happens if I want to list the company name above the price? How do I access that information in Doctrine, and furthermore, in Twig?
I will translate what you've said above into code that I would effectively write if I was you:
So, I assume that, along with the mapping you defined , your company category entity is called 'Company_category', your dog food entity is called 'Dog_food'.
I would pass the id of the company_category to an action in my controller,
then, I would retrieve all the companies that belong to that company_category, something like this:
public function xyzAction($id){
$companies=$this->getDoctrine()->getRepository('XYZYourBundle:Company')
->findBy(array('company_category'=>$id));
//Hold on I will just complete it
}
Then I would retrieve all the dog foods objects from my DB, that the company's exist in $companies, the result returned in the first line of code,
to do this, I would first:
1-Define my own criteria:
this would help you define a complex , powerful conditions and it's easy to use.
2-Use my criteria to filter the result of the repository, this would be useful
So let's update our action:
public function xyzAction($id){
$companies=$this->getDoctrine()->getRepository('XYZYourBundle:Company')->findBy(array('company_category'=>$id));
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where($criteria->expr()->in('company',$companies));
$dogfoods=$this->getDoctrine()->getRepository('XYZYourBundle:Dog_food')->matching($criteria)
//Hold on I will just complete it
}
and finally render our dogfood objects to a twig template:
public function xyzAction($id){
$companies=$this->getDoctrine()->getRepository('XYZYourBundle:Company')->findBy(array('company_category'=>$id));
$criteria = new \Doctrine\Common\Collections\Criteria();
$criteria->where($criteria->expr()->in('company',$companies));
$dogfoods=$this->getDoctrine()->getRepository('XYZYourBundle:Dog_food')->matching($criteria)
return $this->render('XYZYourBundle:test.html.twig', array(
'dogfoods'=>$dogfoods));
}
Now, let's place ouselves in a twig template, we're gonna iterate through our $dogfoods objects and we will display some information, assuming you defined the needed getters and setters;
{% for dogfood in dogfoods %}
{{dogfood.price}}
{{dogfood.company}}
{% endfor %}
Edit :
you can get the company's name by either:
-implement a tostring method that returns the company's name and in twig use
{{dogfood.company}}
or
-in twig, use
{{dogfood.company.name}}
Hope this helps you, if something doesn't work just tell me.

MDS business rule

I am new with MDS, and I have a question about one to many relation mapping in MDS
I have a product, contains descriptions in multiple languages. I have created two entities with derived hierarchy structure: product (P_ID, P_name)and Addtional description(P_ID, P_Name_in_German, P_name_in_English).
Additonal description is a drop down from product table from MDS UI, but I only want to populate info that releated with its same P_ID. How can I achieve that? Can I use business rules here and how it should look like?
(2012 Master data service' web interface)
I have also faced a similar problem. I have decided to add multiple fields for languages and apply business rules for them. Let's see how it would work for your entity:
Product entity
{
Code
Name
Name_in_English (text)
Name_in_German (text)
Default_language (domain based, points to Languages entity)
}
Languages entity
{
Code
Name
}
Add values to the Languages entity:
Code = "EN", Mane = "EN"
Code = "DE", name = "DE"
Now we need to add the following business rules to the Product entity:
BR1:
{
IF Condition - Equals: Default_language equals "EN"
THEN Action - Change value: Name = Name_in_English
}
BR2:
{
IF Condition - Equals: Default_language equals "DE"
THEN Action - Change value: Name = Name_in_German
}
After that You will see the product names in your product entity only in proper language which is chosen by drop-down field Default_language.
The second option:
If You want user to see only the Name field and don't want him to see additional fields,
You can hide those fields (Name_in_English and Name_in_German) by setting their width to zero.
Moreover, You can use attribute groups to separate two modes of view:
first mode (for the regular user) - You see only Code and Name
second mode (for the administrator) - You see fields: Name_in_English, Name_in_German, Default_language.
For it to work You need to create two attribute groups:
1) attribute group "EN" (add attributes Name and Code to the group)
2) attribute group "DE" (add attributes Name_in_English, Name_in_German, Default_language to the group)
Hope something of that is helpful!
You're missing something. You said you have a derived hierarchy structure but you don't show a domain based attribute on the Additional description entity with a pointer back to the Product entity. Perhaps you were thinking that the P_ID for Additional Description is the pointer back but it isn't (based on your explanation). It is the entity Code identifier for the Additional Description entity and not the key you need that points back to Product. Perhaps you meant One to One. One to Many implies you have a separate Parent_P_ID back to the Product entity.
Add the Domain Based Parent_P_ID attribute to the Additional Description entity and then reconstruct your derived hierarchy structure. This may not be that helpful because I think you have left something out of the explanation of what you are trying to do.
Hi Cocunuts,
We can not assign derived hierarchy to entities in MDS 2012,but we can achieve this in MDS 2016 , Your Domain based attribute(Drop down) cannot be further filtered in MDS 2012.

Assigning Users to Profiles

I have an mvc app where I want to assign a user to a profile. I have two multiline select lists (i.e <select size="10"></select>). One for profiles the user is part of and one for the available profiles the user can become part of.
I am using JavaScript with two buttons to move the items between the two lists. When the form submits the Request.Form object only knows about options that are selected in either list but I want to know all the items that are in the 'profiles the user is part of' list.
What can I do to acheive this? I was thinking of using a hidden field and placing the values in the field when they are moved to the 'profiles the user is part of' list. (i.e. <input type="hidden" name="user_profiles" values="3,8,12">).
What do you think? Is there a better way to do this? Thanks.
You could have a script to select everything in the list on the submit event.
Pass in the FormCollection to the action. Then update the model and store the data.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveProfile(FormCollection collection){
// collection is a key based collection so pass the key or the control name to get
// its value
Profile p = new Profile();
p.UserId = collection["UserId"];
p.Name = collection["Name"];
p.Birthdate = collection["Birthdate"];
p.ProfileOption = collection["ProfileOption"];
// DB logic should be done in a service but this is a very simplified example
profileRepository.Update(p);
return View();
}