PHP models : one or more setters by database action? - php

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).

Related

What is a macro in laravel Macroable

please can anyone help me understand what a macro is in Laravel Macroable trait, reading this documentation https://laravel.com/api/5.4/Illuminate/Support/Traits/Macroable.html only tells me how to use but why do I use it, what is it meant for.
It is for adding functionality to a class dynamically at run time.
use Illuminate\Support\Collection;
Collection::macro('someMethod', function ($arg1 = 1, $arg2 = 1) {
return $this->count() + $arg1 + $arg2;
});
$coll = new Collection([1, 2, 3]);
echo $coll->someMethod(1, 2);
// 6 = 3 + (1 + 2)
echo $coll->someMethod();
// 5 = 3 + (1 + 1)
We have 'macroed' some functionality to the Collection class under the name someMethod. We can now call this method on the Collection class and use its functionality.
We just added a method to the class that didn't exist before without having to touch any source files.
For more detail of what is going on, please check out my article on Macros in Laravel:
asklagbox - blog - Laravel Macros
It allows you to add new functions. One call to ::macro adds one new function. This can be done on those of the internal framework classes which are Macroable.
This action of adding the function to the class is done at run time. Note there was/is an already existing perfectly good name for this action, which isn't the word "macro", which I'll explain at the end of this post.
Q. Why would you do this?
A. If you find yourself juggling with these internal classes, like
request & response, adding a function to them might make your code more
readable.
But as always there is a complexity cost in any
abstraction, so only do it if you feel pain.
This article contains a list of the classes you can add functions to using the static call "::macro"
Try not to swallow the word macro though, if you read that article - if you're like me it will give you big indigestion.
So, let's now add one extra function to an internal framework class. Here is the example I have just implemented:
RedirectResponse::macro('withoutQuery', function() {
return redirect()->to(explode('?', url()->previous())[0]);
});
This enables me in a controller to do this:
redirect()->back()->withoutQuery();
(You can just do back() but I added redirect() to make it clear).
This example is to redirect back and where the previous route was something like:
http://myapp.com/home?something=something-else
this function removes the part after '?', to redirect to simply:
http://myapp.com/home
I did not have to code it this way. Indeed another other way to achieve this is for me to put the following function in the base class which all controllers inherit from (App\Http\Controllers\Controller).
public function redirectBackWithoutQuery()
{
return redirect()->to(explode('?',url()->previous())[0]);
}
That means I can in any controller do this:
return $this->redirectBackWithoutQuery();
So in this case the "macro" lets you pretend that your new function is part of an internal framework class, in this case the Illuminate/RedirectResponse class.
Personally I like you found it hard to grasp "laravel macros". I thought that because of the name they were something mysterious.
The first point is you may not need them often.
The second point is the choice of the name ::macro to mean "add a function to a class"
What is a real macro?
A true macro is a concept unique to Lisp. A macro is like a function but it builds and returns actual code which is then executed. It is possible to write a function in other languages which returns a string which you then execute as if it was code, and that would be pretty much the same thing. However if you think about it you have all of the syntax to deal with when you do that. Lisp code is actually structured in lists. A comparison might be imagine if javascript was all written as actual json. Then you could write javascript, which was json, which returned json, which the macro would then just execute. But lisp is a lot simpler than json in terms of its syntax so it is a lot easier than what you just imagined. So, a true lisp macro is one of the most beautiful and amazing things you can encounter.
So why are these add-a-function things in laravel called macros?
That's unknown to me I'm afraid, you'd have to ask the author, but I asked myself what they really do and is there already a name for that.
Monkey Patches
TL;DR laravel's ::macro could more accurately be described as monkey patch
So if using laravel ::macro calls, I personally decided to create a MonkeyPatchServiceProvider and put them all there, to reduce unnecessary confusion for myself.
I realise the name might sound a bit derogatory, but that's not intended at all.
It's simply because there's already a name for this, and we have so much terminology to deal with why not use an existing name.

Is it bad practice to use the same method for SAVE and UPDATE?

I'm using laravel but it's not important, when you create a controller with laravel command line tool, it puts 4 default function in there for create and update.
create and store for save
edit and update for well update!
This is what laravel suggest for Shop controller.
class ShopController extends Controller
{
public function create()
{
// return create view
}
public function store(Request $request)
{
// save a shop
}
public function edit($id)
{
// find a shop , return edit view
}
public function update(Request $request, $id)
{
// find the shop with id , update the shop
}
}
But I like to use the same methods for showing view and store/update my row and avoid writing lots of extra code.
class ShopController extends Controller
{
public function create($id = 0)
{
return view('shop-create' , ['edit'=> Shop::find($id)]);
}
public function store(Request $request , $id = 0 )
{
$whitelist = [
'title'=>'required',
'phone'=>'present|numeric' ,
'address'=>'present' ,
];
$this->validate($request, $whitelist );
$shop = Shop::findOrNew($id) ;
// find a shop with given id or create a new shop instance
foreach($whitelist as $k=>$v)
$shop->$k = $request[$k];
$shop->save();
}
}
Naturally I go with what I like (second option), but since laravel suggest the first way, just out of curiosity is there any reason why I shouldn't do it like this? Is this considered bad practice in any way?
Nothing wrong, but you code will be harder to understand, IMHO.
e.g.:
What does this method do? It's called create, but it also edits?
The view is called shop-create but it also edits?
Passing a 0 parameter as default for id and trying to find it every time is unnecessary.
public function create($id = 0)
{
return view('shop-create' , ['edit'=> Shop::find($id)]);
}
Although you're thinking that you are simplifying your code, you are turning it more complicated since you are breaking the Single Responsibility principle from SOLID.
It's easier to understand if you have something like the Laravel suggestion.
Also you keep a very common pattern that any Laravel developer will understand, so you can hire someone to take care of your code and do not worry if he will understand.
There is nothing wrong with doing it your way. The "laravel" way you mention is when you create a Restful resource controller and is simply one way to tackle it.
I guess those controller methods were picked because they line up nicely to a "restful" type of controller. If you were to be building a true rest api, then how you do it becomes far more strict from a standards point of view (not imposed by laravel, but line up better to the laravel way).
If you aren't creating a public facing api, or something that is going to be consumed by external entities, then I say design your controllers that work best for you and your team
This is how i usually do it, this way you can still have different validation by using the requests and it's still clear (imo) what the functions do.
public function store(AdminPartnerRequest $request)
{
return $this->handleCreateOrUpdate($request);
}
public function update(AdminPartnerRequest $request, $id)
{
return $this->handleCreateOrUpdate($request,true, $id);
}
private function handleCreateOrUpdate($request, $edit = false, $id = null)
{
if ($edit){
$partner = Partner::find($id);
} else{
$partner = new Partner();
}
$partner->name = $request->input('name');
$partner->picture = $request->input('image');
$partner->save();
return \Redirect::route('admin.partners.index');
}
Using same function for save() and update() is good idea but at the same time it will increase complexity .. One point is If in future you want to change anything you need to change it only at one place.
But at the same time you need to take some extra care.
As your function should be more dynamic.
1) Multiple records manipulation : you may require to update more than one raws at the same time so your function should be enough flexible to insert/update single/multiple values by the same function. Meaning , single query should be fired for multiple records in both the cases.
2) Validation if value already exist : When you are going to check some validation ...
in insert case you only need to check if the value is exist in db or not
when in update case you need to check with exclusion of current id
e.g.
for insert case
$this->validate($request, [
'email' => 'required|string|email|unique:tablename,email'
]);
for update case
$this->validate($request, [
'email' => 'required|string|email|unique:tablename,email,'.$id.',id'
]);
And at last very small point but need to be considered ..
3) Success message : At the time of insertion message should be "added successfully" and at updation time Record "updated successfully"
Small project, do whatever you want. Large with other developers, follow the conventions.
Coding conventions are a set of guidelines for a specific programming language that recommend programming style, practices, and methods for each aspect of a program written in that language. These conventions usually cover file organization, indentation, comments, declarations, statements, white space, naming conventions, programming practices, programming principles, programming rules of thumb, architectural best practices, etc. These are guidelines for software structural quality. Software programmers are highly recommended to follow these guidelines to help improve the readability of their source code and make software maintenance easier. Coding conventions are only applicable to the human maintainers and peer reviewers of a software project. Conventions may be formalized in a documented set of rules that an entire team or company follows, or may be as informal as the habitual coding practices of an individual. Coding conventions are not enforced by compilers. -- https://en.wikipedia.org/wiki/Coding_conventions
I used this method in a last project of mine, we called the store() and update() function manage() instead and had a getManage() which would use the same view for creating and editing. I liked this method a lot yet came across a few things worth noting. Sadly the cons outway the pros if you ever have to face those issues :(
Pros:
Smaller code - No longer do you have duplicate lines of code in your store() and update() function.
Faster to re-use with basic models - ctrl+c ctrl+v ctrl+f ctrl+r if you know what I mean.
Easier to add/change input values - An extra value would not mean having to change store() and update() to make sure they both utilize the extra input.
One function to rule them all - As long as you are not doing anything special, you can even define one function for everything. Need to change something? You've got one function, no worries.
Cons:
Code is harder to understand for others (or an older you) - If someone is new to this method or hasn't used it in a while, understanding what happens within your function is a little harder than having two separate ones.
Validation is a nuisance - As stated in this answer validation may be different for create and update. Meaning you may sometimes have to write two validations which will eventually lead to messy code and we don't want that!
Value insertion wasn't as cool as I thought - If you want to use the same predefined array to create or update then you may run into the problem of wanting to insert values on create yet never want to update them. This eventually led to either ugly if statements or two predefined arrays.
Eventually it's up to what you're going to make and what you want to do. If you have a basic website which will manage blog posts and pages then have no worries going for a shared store() and update() function. Yet if you're creating a huge CMS with many models, relations and different create and update input values (which may mean different validation) then I'd go with what Laravel recommends. It would be much easier to maintain in the long run and you won't have to deal with headaches, hacky fixes and unclean code.
Whatever you do, don't do both in different controllers! That would be confusing.
By the way, if you're wondering what kind of project I had - it was a huge CMS. So even though it was very useful and easy in some cases, it was sadly not worth it.
Nothing wrong, but in that case you have to maintain proper comments that specify that your function perform add / edit and for that you are using some variable like $id or some thing else. If it is available than you can update the record otherwise insert it.

Is it a good practice to define expectation in dataProvider

quick example of dataProvider:
return [
['180d-1pc', '6m-1pc'],
]
and test:
public function test_convert($title, $expected)
{
$uut = new Converter();
$this->assertEquals($expected, $uut->convertDayTitle($title));
}
(simply put: test if we convert 180d(days) to 6m(months))
as you can see - in data provider there are input data defined and also an expected output.
This works fine in many cases, but I keep having this feeling that maybe it's not the best idea. So I'm wondering if could be considered bad practice. If so - when I will see it was a bad idea to do it?
One example is, when you would like to use the same dataProvider in two tests - should you define two expected values and use one of them?
Quick example (I just made that up, so don't pay attention that I make product object just from title ;)):
public function test_gets_discount_when_licence_period_longer_than_1year($title, $expectedTitle, $expectedDiscount) {
$prod = new Product($title);
$this->assertEquals($expectedDiscount, $product->hasDiscount();
}
How to make this more elegant?
What you're doing is totally fine. Data providers can, and should, include the expected test result.
About the issue of reusing the same dataProvider for 2 tests, and only using some fields: I'd say that if the fields are related (i.e. they are properties of the same object), it can be acceptable.
If you feel the dataProvider is getting too big and complex because 2 tests must use data from it, just create 2 separate dataProviders, and use another private common method for building the common data.

insert/delete query design of website

I wonder what would be the professional way to handle insert/delete requests of a website?
Right now, what I have is I inserted two <input type = "hidden"/> on each form where one hidden's value correspond to a function it needs and the other is the parameter of this function. So when the form submits, I have a post.php file that handles ALL insert/delete requests that simply invokes the value of the hiddens via call_user_func() in PHP. Like so:
<input name = "arg" value = "{$id}" type = "hidden"/>
<input name = "call" value = "delete_member" type = "hidden"/>
call_user_func($_POST['call'], $_POST['arg']);
I'm having doubts on how sensible this solution is because I found out that the hiddens aren't actually hidden in the source on the client-side.
My first solution was to basically have a lot of conditionals checking for which function to invoke but I really hated that a lot so I changed it with this solution.
I wonder what are the better ways I can do this, or maybe how the professionals do it? Handling insert/delete queries.
I would consider this a very bad way to call actions from the client side.
For one this data is put into the HTML which will always be viewable and editable by the client. As such this means you cannot trust the data you receive from the client and as such you cannot trust the function they are calling.
Another point to re-inforce my previous idea. You say you run validation to make sure it is a function and all that, but you have a problem. Closures return true on these functions (since they are functions and methods and they exist). So a user can put a anon function as the value of your hiden field and actually run whatever they want on your server.
As others say I would recommend looking into MVC. Look into how Yii/CodeIgniter/Zend/Lithium/Kohana/etc do this and how they route.
An example of how routing for actions such as deletion is done by my favourite framework, Yii:
class UserController extends CController{
public function actionDelete($id = null){
if($id===null){ return false; }
}
}
Then the form/link calls /user/delete?id=2 which makes index.php route to the userController and use the actionDelete function inside the user controller, running it's code. Of course this is a very simplified version and it gets a lot more complex to stop vulnerabilities.
You may also wish to look into CSRF validation.
The most common way is to just call a function that takes care of one form at the time like this example for submitting a blog message:
blog.php
if(isset($_POST['submit'])) {
save_message();
} else {
display_form();
}
function display_form()
{
?>
<form action="blog.php"> etc etc....
<?php
}
function save_message()
{
//security checks and inserts etc
$_SESSION['message'] = 'Form saved succesfully';
header('location: blog_overview.php');
}
This is according to me a practish, but you might want to checkout frameworks like Codeigniter and Kohana since the above code is functional (and to me outdated). Read some tutorials about OOP (Object Oriented Programming) and MVC (Model View Controller). It might seem alot of work, but if you really want it to do it right it is worth the time and effort.

Using the Data Mapper Pattern, Should the Entities (Domain Objects) know about the Mapper?

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)

Categories