I have two types of user: client, manager. are stored in separated tables.
Each of then have unique nickname.
So, I need to open profile by nickname.
For Client is:
$client = Client::where("nickname", $nickname)
For Manager is:
$manager = Manager::where("nickname", $nickname)
So, I try to make universal function that dewtect where is client and manager and execute the appropriate query.
How can I improve this code and detect type of user only by nickename?
You should use one model for this, I guess it would be best way to handle clients and managers.
If for some reason you want to use two models, you can create method and put it in Client model since most of queries will be for clients:
public function getClientOrManagerByNickname($nickname) {
$client = $this->where('nickname', $nickname)->first();
return is_null($client) ? (new Manager)->where('nickname', $nickname)->first() : $client;
}
This code will create one query if client is found and will return this client. Or it will create two queires and will return manager. If there is no client and manager with this nickname, it will return null.
As I said I would suggest something a bit more complex but that to me would be less prone to make mistakes because of a confusion on the Model returned.
I would suggest something along this:
$modelname = getModelName(); $values = $modelName::where(1);
Where the function getModelName() would just return the ModelName.
I'm aware that this implies going through the DB another time and it increases the cost of the operation but I would rather loss a bit of performance and have more coherence in the code.
That's my personal opinion though.
Related
I have an DailyReport Entity in my Domain Layer. There are some fields in this object:
reportId
userId
date
tasks - Collection of things that user did in given day;
mood - how does the user felt during the whole day;
Also, there are some methods in my Application Service:
DailyReportService::addTaskToDailyReport
DailyReportService::setUserMoodInDailyReport
The thing is that both of these methods require DailyReport to be created earlier or created during function execution. How to deal with this situation?
I have found 2 solutions:
1 Create new DailyReport object before method dispatching, and after that pass reportId to them:
//PHP, simplified
public function __invoke() {
$taskData = getTaskData();
/** #var $dailyReport DailyReport|null **/
$dailyReport = $dailyReportRepository->getOneByDateAndUser('1234-12-12', $user);
//there were no report created today, create new one
if($dailyReport === null) {
$dailyReport = new DailyReport('1234-12-12', $user);
$dailyReportRepository->store($dailyReport);
}
$result = $dailyReportService->addTaskToDailyReport($taskData, $dailyReport->reportId);
//[...]
}
This one requires to put a more business logic to my Controller which i want to avoid.
2: Verify in method that DailyReport exists, and create new one if needed:
//my controller method
public function __invoke() {
$taskData = getTaskData();
$result = $dailyReportService->addTaskToDailyReport($taskData, '1234-12-12', $user);
//[...]
}
//in my service:
public function addTaskToDailyReport($taskData, $date, $user) {
//Ensure that daily report for given day and user exists:
/** #var $dailyReport DailyReport|null **/
$dailyReport = $dailyReportRepository->getOneByDateAndUser();
//there were no report created today, create new one
if($dailyReport === null) {
$dailyReport = new DailyReport($date, $user);
$dailyReportRepository->store($dailyReport);
}
//perform rest of domain logic here
}
This one reduces complexity of my UI layer and does not expose business logic above the Application Layer.
Maybe these example is more CRUD-ish than DDD, but i wanted to expose one of my use-case in simpler way.
Which solution should be used when in these case? Is there any better way to handle get-or-create logic in DDD?
EDIT 2020-03-05 16:21:
a 3 example, this is what i am talking about in my first comment to Savvas Answer:
//a method that listens to new requests
public function onKernelRequest() {
//assume that user is logged in
$dailyReportService->ensureThereIsAUserReportForGivenDay(
$userObject,
$currentDateObject
);
}
// in my dailyReportService:
public function ensureThereIsAUserReportForGivenDay($user, $date) {
$report = getReportFromDB();
if($report === null) {
$report = createNewReport();
storeNewReport();
}
return $report;
}
//in my controllers
public function __invoke() {
$taskData = getTaskData();
//addTaskToDailyReport() only adds the data to summary, does not creates a new one
$result = $dailyReportService->addTaskToDailyReport($taskData, '1234-12-12', $user);
//[...]
}
This will be executed only when user will log in for the first time/user were logged in yesterday but this is his first request during the new day.
There will be less complexity in my business logic, i do not need to constantly checking in services/controllers if there is a report created because this has been executed
previously in the day.
I'm not sure if this is the answer you want to hear, but basically I think you're dealing with accidental complexity, and you're trying to solve the wrong problem.
Before continuing I'd strongly suggest you consider the following questions:
What happens if someone submits the same report twice
What happens if someone submits a report two different times, but in the second one, it's slightly different?
What is the impact of actually storing the same report from the same person twice?
The answers to the above questions should guide your decision.
IMPORTANT: Also, please note that both of your methods above have a small window where two concurrent requests to store the rerport would succeed.
From personal experience I would suggest:
If having duplicates isn't that big a problem (for example you may have a script that you run manually or automatically every so often that clears duplicates), then follow your option 1. It's not that bad, and for human scale errors should work OK.
If duplicates are somewhat of a problem, have a process that runs asynchronously after reports are submited, and tries to find duplicates. Then deal with them according to how your domain experts want (for example maybe duplicates are deleted, if one is newer either the old is deleted or flagged for human decision)
If this is part of an invariant-level constraint in the business (although I highly doubt it given that we're speaking about reports), and at no point in time should there ever be two reports, then there should be an aggregate in place to enforce this. Maybe this is UserMonthlyReport or whatever, and you can enforce this during runtime. Of course this is more complicated and potentially a lot more work, but if there is a business case for an invariant, then this is what you should do. (again, I doubt it's needed for reports, but I write it here in the care reports were used as an example, or for future readers).
I'm using the MVC pattern in a CodeIgniter PHP project and I'm wondering what's the best way to build methods of the models in order to ensure readability of the code and scalability.
In short, is this better to do this ? :
public function set_account_state($new_state) {
// UPDATE a database record state to $new_state [0, 1 or 2]
}
Or this ? :
public function reject_account() {
// UPDATE a database record state to 0
}
public function accept_account() {
// UPDATE a database record state to 1
}
public function pending_account() {
// UPDATE a database record state to 2
}
Or maybe another way ?
Also, is there a good practice for function naming in such cases ?
function set_account_state($state)
This is better when you have only one task and that is to change the state.
But if in future you might have to do different tasks before you change the account state then you need three different methods.
You can still have that original method to change the state of the account and call it from your three methods.
Function naming should include a verb with nouns to make it clearer.
function pending_account() is not that clear, function keep_account_pending() is I feel a better way to name it.
In my opinion, the second way is the better way.
Using names that indicate what will the method do can improve the readability of the code.
As your code grows bigger, it's easy to forget what number represents what state. That does not happen with names because a method called reject_account() will indicate that account will be rejected better then a number passed as argument to a function.
Also when other people work on your code, it will be easier for them to understand what is going on when a method is called. Having said that, it's better for one understand that the client account is accepted when one sees a method called accept_account() then when one sees set_account_state(1).
I'm working on an application written in PHP. I decided to follow the MVC architecture.
However, as the code gets bigger and bigger, I realized that some code gets duplicated in some cases. Also, I'm still confused whether I should use static functions when quering the database or not.
Let's take an example on how I do it :
class User {
private id;
private name;
private age;
}
Now, inside this class I will write methods that operate on a single user instance (CRUD operations). On the other hand, I added general static functions to deal with multiple users like :
public static function getUsers()
The main problem that I'm facing is that I have to access fields through the results when I need to loop through users in my views. for example :
$users = User::getUsers();
// View
foreach($users as $user) {
echo $user['firstname'];
echo $user['lastname'];
}
I decided to do this because I didn't feel it's necessary to create a single user instance for all the users just to do some simple data processing like displaying their informations. But, what if I change the table fields names ? I have to go through all the code and change those fields, and this is what bothers me.
So my question is, how do you deal with database queries like that, and is it fine to use static functions when querying the database. And finally, where is it logical to store those "displaying" functions like the one I talked about ?
Your approach seems fine, howerver I would still use caching like memcached to cache values and then you can remove static.
public function getUsers() {
$users = $cacheObj->get('all_users');
if ($users === false) {
//use your query to grab users and set it to cache
$users = "FROM QUERY";
$cacheObj->set('all_users', $users);
}
return $users;
}
(M)odel (V)iew (C)ontroller is a great choice choice, but my advice is look at using a framework. The con is they can have a step learning curve, pro is it does a lot of heavy lifting. But if you want to proceed on your own fair play, it can be tough to do it yourself.
Location wise you have a choice because the model is not clearly define:
You'll hear the term "business logic" used, basically Model has everything baring views and the controllers. The controllers should be lean only moving data then returning it to the view.
You model houses DB interaction, data conversions, timezone changes, general day to day functions.
Moudle
/User
/Model
/DB or (Entities and Mapper)
/Utils
I use Zend and it uses table gateways for standard CRUD to avoid repetition.
Where you have the getUsers() method you just pass a array to it, and it becomes really reusable and you'd just have different arrays in various controller actions and it builds the queries for you from the array info.
Example:
$data = array ('id' => 26)
$userMapper->getUsers($data);
to get user 26
enter code here
$data = array ('active' => 1, 'name' => 'Darren')
$userMapper->getUsers($data);`
to get active users named Darren
I hope this help.
Suppose I have 2 identical table having same structure(Call it 'tableA' & 'tableB').
I want to save certain data on table 'A' and certain data on table 'B'.
NOW I want to use the same MODEL for both the table.
I want to change the table linked with the Model(say 'ModelM') to change dynamically based on condition at the controller.
e.g.
In controller:- //sample code
function saveProduct(){
$this->loadModel('ModelM');
if(condition){
$this->ModelM->useTable = 'A';
}else{
$this->ModelM->useTable = 'B';
}
$this->ModelM->save($this->data);
}
ADDITION ON 14th JANUARY 2011
Following is the copy/paste of code I am working on:
function experiment(){
$tableName = 'temp_table'.'1234';
mysql_query('CREATE TABLE '.$tableName.' LIKE temp_home_masters');
$sql = $this->createInsertQuery($new_arr,$tableName);
$status = mysql_query($sql);
if($status){
echo "saved successfully";
}else{
echo "error";
}
$this->NewHomeMaster->setSource($tableName);//NewHomeMaster was previously attached to a different table , here I want to change the tableName the model linked with dynamically.Model 'NewHomeMaster' already exists and uses a table ...Here I am willing to link this model to the newly created tempory table.//
$home_details=$this->paginate('NewHomeMaster',array($new_conditions));
mysql_query('DROP table '.$tableName);
}
UNFORTUNATELY THIS DOES NOT WORK...
I originally set out to figure a solution to your problem, but the more I think about it, I believe your logic is flawed.
I can see how the fact that the tables are similar or identical can lead you to the decision of using a single model to interact with both tables. However, when you look at a what a model is supposed to be (In CakePHP basically an interface to a table), it doesn't make sense to switch back and forth.
The CakePHP docs explain models like this:
In object-oriented programming a data model is an object that represents a "thing", like a car, a person, or a house.
In your example, you really have two separate "things" that look exactly the same. Therefore, they should have their own models.
If your models are really going to have the exact same methods, then "the CakePHP Way" would be to define a custom Behavior that encapsulates your shared methods. Then attach the behavior to both models.
Then you can load the model you need in the Controller condition:
private $DynamicModel;
public function saveProduct() {
if (condition) {
App::import('Model', 'ModelZ');
$this->DynamicModel = new ModelZ;
} else {
App::import('Model', 'ModelY');
$this->DynamicModel = new ModelY;
}
$this->DynamicModel->save($this->data);
}
Do this on your controllers before filter function:
$this->CakePHPModelName->setSource('table_name');
This will use different table.
Source: http://www.mainelydesign.com/blog/view/changing-cakephps-model-usetable-on-fly
The situation is tricky, as Stephen describes it, because your approach somewhat violates the MVC conventions.
However, if you're willing to go to the dark side of custom hacks, you could consider creating you own customized Datasource in CakePHP that handles this kind of logic for you. An option is to extend a given Datasource (presumably the MySQL one) with you own custom logic that aims to perform some prelimary filtering/conditioning before interacting with the database. Not that clean because the logic is placed in the wrong scope, but could work. Have a look here for a start: http://book.cakephp.org/view/1075/DataSources
Alternatively, you could create two different models and make them share the same logic using a behavior. This kinda limits you to take the choice of model earlier in the flow (and thus doesn't only affect the location of data storage), but might be a possibility.
I'm working with Doctrine2 for the first time, but I think this question is generic enough to not be dependent on a specific ORM.
Should the entities in a Data Mapper pattern be aware - and use - the Mapper?
I have a few specific examples, but they all seem to boil down to the same general question.
If I'm dealing with data from an external source - for example a User has many Messages - and the external source simply provides the latest few entities (like an RSS feed), how can $user->addMessage($message) check for duplicates unless it either is aware of the Mapper, or it 'searches' through the collection (seems like an inefficient thing to do).
Of course a Controller or Transaction Script could check for duplicates before adding the message to the user - but that doesn't seem quite right, and would lead to code duplication.
If I have a large collection - again a User with many Messages - how can the User entity provide limiting and pagination for the collection without actually proxying a Mapper call?
Again, the Controller or Transaction Script or whatever is using the Entity could use the Mapper directly to retrieve a collection of the User's Messages limited by count, date range, or other factors - but that too would lead to code duplication.
Is the answer using Repositories and making the Entity aware of them? (At least for Doctrine2, and whatever analogous concept is used by other ORMs.) At that point the Entity is still relatively decoupled from the Mapper.
Rule #1: Keep your domain model simple and straightforward.
First, don't prematurely optimize something because you think it may be inefficient. Build your domain so that the objects and syntax flow correctly. Keep the interfaces clean: $user->addMessage($message) is clean, precise and unambiguous. Underneath the hood you can utilize any number of patterns/techniques to ensure that integrity is maintained (caching, lookups, etc). You can utilize Services to orchestrate (complex) object dependencies, probably overkill for this but here is a basic sample/idea.
class User
{
public function addMessage(Message $message)
{
// One solution, loop through all messages first, throw error if already exists
$this->messages[] $message;
}
public function getMessage()
{
return $this->messages;
}
}
class MessageService
{
public function addUserMessage(User $user, Message $message)
{
// Ensure unique message for user
// One solution is loop through $user->getMessages() here and make sure unique
// This is more or less the only path to adding a message, so ensure its integrity here before proceeding
// There could also be ACL checks placed here as well
// You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding
if ($this->doesUserHaveMessage($user,$message)) {
throw Exception...
}
$user->addMessage($message);
}
// Note, this may not be the correct place for this function to "live"
public function doesUserHaveMessage(User $user, Message $message)
{
// Do a database lookup here
return ($user->hasMessage($message) ? true
}
}
class MessageRepository
{
public function find(/* criteria */)
{
// Use caching here
return $message;
}
}
class MessageFactory
{
public function createMessage($data)
{
//
$message = new Message();
// setters
return $message;
}
}
// Application code
$user = $userRepository->find(/* lookup criteria */);
$message = $messageFactory->create(/* data */);
// Could wrap in try/catch
$messageService->sendUserMessage($user,$message);
Been working with Doctrine2 as well. Your domain entity objects are just that objects...they should not have any idea of where they came from, the domain model just manages them and passes them around to the various functions that manage and manipulate them.
Looking back over, I'm not sure that I completely answered your question. However, I don't think that the entities themselves should have any access to the mappers. Create Services/Repositories/Whatever to operate on the objects and utilize the appropriate techniques in those functions...
Don't overengineer it from the onset either. Keep your domain focused on its goal and refactor when performance is actually an issue.
IMO, an Entity should be oblivious of where it came from, who created it and how to populate its related Entities. In the ORM I use (my own) I am able to define joins between two tables and limiting its results by specifying (in C#) :
SearchCriteria sc = new SearchCriteria();
sc.AddSort("Message.CREATED_DATE","DESC");
sc.MaxRows = 10;
results = Mapper.Read(sc, new User(new Message());
That will result in a join which is limited to 10 items, ordered by date create of message. The Message items will be added to each User. If I write:
results = Mapper.Read(sc, new Message(new User());
the join is reversed.
So, it is possible to make Entities completely unaware of the mapper.
No.
Here's why: trust. You cannot trust data to act on the benefit of the system. You can only trust the system to act on data. This is a fundamental of programming logic.
Let's say something nasty slipped into the data and it was intended for XSS. If a data chunk is performing actions or if it's evaluated, then the XSS code gets blended into things and it will open a security hole.
Let not the left hand know what the right hand doeth! (mostly because you don't want to know)