Let me start off by saying I'm an intermediate level PHP coder who's learning OOP. I've got a site running, but I would like to break my code up to implement a more flexible design pattern...and because OOP is just plain awesome.
In my original code, I used switch statements to call functions that correspond with the user request.
$request = (string) $_GET['fruit'];
switch ($request) {
case 'apply':
Get_Apple();
default:
Error_Not_A_Fruit();
exit;
}
This makes it highly inflexible and requires me to change the code at multiple locations for adding new options the user may request.
I'm thinking about changing it to a Polymorphic class call. I'm using composer, so I've got my Objects setup to autoload with PSR-4 standards. So the answer seems simple, if the user request is "apple," I could create
$request = (string) $_GET['fruit'];
$product = new Product\$request;
But, if a user manually enters something that doesn't exists...say "orange," what method would I use to white list the user's input? Like I said, this is my first venture into OOP and would love to pick up design standards that you guys use. I'm thinking encapsulating the block inside a try{} & catch(){} block, but is that the way it should be done?
Any advise would be greatly appreciated :)
Cheers,
Niro
Update: I'd like to make it clearer because it may not have been before. I'm looking for the approach to doing this in such a way that one could add new Objects of Subclass Product (implementing a Product Interface). That way I can add different Product types without changing code everywhere.
Well, you can always use class_exist('Product\\Apple').
class_exists takes 2 arguments:
Class Name (full, with namespace)
Boolean value, whether to try and autoload the class or not. Default is true.
The function returns a boolean.
So you write
$fullClassName = "Product\\$request";
if(class_exists($fullClassName )) {
$product = new $fullClassName();
}
else {
//error here
}
Related
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.
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.
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
I'm a big fan of Doctrine, but in the past have primarily used it for an easier and more OO way of accessing my database. Lately though I've found myself wanted to do MORE with the models though, to make my code more semantic or logical, and easy to use.
For an example, let's use a Stack Overflow Question and corresponding Answers. A Question can contain one or more Answers. In the past, I might do something like this (everything below is sudo code btw):
/*
* METHOD ONE: Note that this is IMO not that semantic or OO
*/
Class QuestionController{
//.......
public function addAnswer($whichQuestionId, $answerTitle, $answerCopy, $answerAuthorId)
{
//Look, I'm validating stuff here in my controller action
if(!strlen($answerTitle) || !strlen($answerCopy) || !strlen($answerAuthorId))
throw new \Exception('Invalid answer stuff!!');
//Notice here I'm actually doing some DB quering with
$author = $entityManager->find('\StackOverflow\User', $answerAuthorId);
if($author->getIsBanned())
throw new \Exception('This is user can not post answer, they are banned!');
//OK, things are valid, now we're going to load then question and create a new answer
$question = $entityManager->find('\StackOverflow\Question', $whichQuestionId)
$answer = new \StackOverflow\Answer;
$answer->setAuthor($author);
$answer->setTitle($answerTitle);
$answer->setContent($answerCopy);
$question->addAnswer($answer);
//Let's pretend we persisted everything correctly...
//Done!
}
//.......
}
Class \StackOverflow\Answer{
//Normal auto generated stuff here
}
Class \StackOverflow\Qusetion{
//Normal auto generated stuff here
}
OK, so note that I'm doing all of my validation in an controller action, which works fine, but if I want to add a answer to a question in a different controller all together then that's not very dry. I could put this validation logic in a "helper" of some sort, but that doesn't seem to be particularly OO either.
I'd RATHER do something like this (not totally well though out, but hopefully you get the idea)
/*
* METHOD TWO: Hopefully a bit more OO...
*/
Class QuestionController{
public function addAnswer($whichQuestionId, $answerTitle, $answerCopy, $answerAuthorId)
{
$result = new \stdObject();
try{
$question = $entityManager->find('\StackOverflow\Question', $whichQuestionId);
$answer = new \StackOverflow\Answer;
$answer->setAuthor($author);
$answer->setTitle($answerTitle);
$answer->setContent($answerCopy);
//THIS IS NEW!
$answer->validate();
//note that perhaps to take this a step futher, in the \StackOverflow\Question class
//I might actually call the validate function, so it's a bit more transaparent
$question->addAnswer($answer);
//Let's pretend we persisted everything correctly...
//Done!
$result->success = true;
$result->message = 'New answer added!';
}
catch(Exception $e)
{
$result->success = false;
$result->message = $e->getMessage();
}
return json_encode($result);
}
}
Class \StackOverflow\Answer{
//ALL NORMAL AUTO GENERATED GETTERS/SETTERS/ETC HERE
//CUSTOM FUNCTION - perhaps called by one of the LifeCycle callback things that Doctrine has?
public function validate()
{
//Look, I'm validating stuff here in my controller action
if(!strlen($this->getTitle()) || !strlen($this->getContent()) || !strlen($this->getAuthor()))
throw new \Exception('Invalid answer stuff!!');
//Notice here I'm actually doing some DB quering INSIDE of another model
$author = $entityManager->find('\StackOverflow\User', $answerAuthorId);
if($author->getIsBanned())
throw new \Exception('This is user can not post answer, they are banned!');
}
}
Class \StackOverflow\Question{
//Normal auto generated stuff here
}
So this is perhaps a bit better I suppose, is this the correct way to do things with Doctrine? Is it OK to call other models within an existing model, or to otherwise use the Doctrine Entity Manager in this way? Is there a cleaner way of doing this?
This specific example is pretty simple, but in real life I have a relationship similar to this but the "Answer" has much more complex and ugly validation, requiring lookups to other tables, getting information about the "Question" for it's validation, etc. It also might update the "User" in our example, for instance adding a new Answer to a Question might update the User's "QuestionsAnswered" value. I'd rather this happen transparently, without having to know it's really happening. I'd like to simply add an answer to a question, and all of the validation and cascading changes happen automatically behind the scenes.
There is no right or wrong answer to this. Its ultimately your choice as to how heavy your models in comparison with controllers.
My suggestion is to pick one way to do it, and stay consistent.
I would make a note that the validation should be related to the object that you are working with. Checking if a user is banned within the answer validation is probably not the best place to do that. Instead you should be checking that data related to the answer is valid.
I'm a little confused as to what is going on here, it looks to me like a method is calling itself? I'm trying to learn about Magento's models. I was working my way back from a helper (catalog/category) and I got to a call on this method "GetCategories". I don't know whats going on here. If anyone could shed light on this code snippet I greatly appreciate it.
getCategories ( $parent,
$recursionLevel = 0,
$sorted = false,
$asCollection = false,
$toLoad = true
){
$categories = $this->getResource()
->getCategories($parent, $recursionLevel, $sorted, $asCollection, $toLoad);
return $categories;
}
Not much to add to #hakra's answer. Just a portion of Magento-specific logic.
So to work with Magento models you should know, that Magento has 2 types of Models: normal models, and resource models (we can call assign Blocks to the models too, as a view models - but that is more connected to the V part of MVC).
The resource models were created as a DB adapters that contain only DB-related logic, and often are connected to some DB table, hence contain the logic for CRUD operations with that table. So you'll see smth like this regularly - for the simplicity someMethod is a part of normal model, but since it contains DB-related logic, all the implementation of the method was moved to the resource model, so the body of someMethod in the regular model will be something like that:
public function someMethod($args)
{
return $this->getResource()->someMethod($args);
}
It is hard to say for the code you've posted. Even both methods share the same name (getCategories) it must not mean that they are of the same class or even object.
If you want to find out you would need to compare:
var_dump($this === $this->getResource());
Apart from that, it is also common in programming recursion that a method calls itself, hence recursion. However for that chunk of code, it would run against the wall.
So technically speaking I would do the assumption that in your example this is not the exact same object method.
Please take note that this answer is independent to Magento, it's just how PHP works generally.