I have been developing a Sports Management website with user, roster, and schedule management using PHP and JS. This app has about 15 database tables to work with altogether. Recently I learned I was doing something majorly wrong: I was using individual PHP scripts as their own "apps".
Example: Individual scripts app
JS
var params = {
action : 'addGame',
date : 'someday',
hometeam : 1,
awayteam : 2
}
$.post('/php/scheduler.php', params);
PHP
class Scheduler extends TableManager{
public function addGame($date, $hometeam, $awayteam){
// do sql stuff
}
public function doAction($request){
switch($request){
case "addGame":
return $this->addGame($_REQUEST['date'], $_REQUEST['hometeam'], $_REQUEST['awayteam'];
break;
}
}
}
if(isset($_REQUEST['action'])){
$scheduler = new Scheduler();
$scheduler->doAction($_REQUEST['action']);
}
I learned that writing a proper PHP application means there should be a single "app" where all requests are routed. This is fine and I've done this - but now I face unknown territory. If I have 15 tables, and they all have a few very specific functions (they all extend TableManager to provide basic table functions, but some are obviously more specific) how can I write a request processor in my main App that handles requests for every table, without getting insanely complex?
Example: Single entry point app
JS
var params = {
action : 'addGame',
table : 'scheduler',
date : 'someday',
hometeam : 1,
awayteam : 2
}
$.post('/php/app.php', params);
PHP
class App {
private $scheduleTable;
public function createTable($name){
switch($name){
case "scheduler":
$this->scheduleTable = new Scheduler();
break;
}
}
public doAction($request){
switch($request){
case "addGame":
$this->createTable('scheduler');
return $this->scheduleTable->addGame($_REQUEST['date'], $_REQUEST['hometeam'], $_REQUEST['awayteam'];
break;
}
}
}
if(isset($_REQUEST['action'])){
$app = new App();
$app ->doAction($_REQUEST['action']);
}
Now I need to start using complex and bloated switch statements, as my tables all inherit TableManager and thus do many of the same actions, but also have unique functions. I'll need to switch on the incoming action, and then switch on the table. Not to mention all of the other features that this App will have (user system, for one). Is the only answer to write a massive switch statement?
From what I understand of your current predicament, yes. You could always use a PHP framework (Google them, there are many) but if you want to use your existing structure then of course you need a switch statement or a way to load the correct models for the page.
Note that you could always use something like this:
<?php
// I assume $request is sanitized.
// Check to see if the class file exists.
if( file_exists( 'class-directory/' . $request . '.inc.php' ) ) {
$class_name = ucfirst( $request );
$this->scheduleTable = new $class_name;
} else {
// Show error page, the request is not valid.
}
?>
Important note: for more security you should use $_POST, $_GET and $_COOKIE instead of $_REQUEST.
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 trying to get a :"var = new class" , but i need this variable to be global in the file (or superglobal) and it needs to be rememberd.
<?php
include 'game.php';
include 'button.php';
include 'beurt.php';
include 'scores.php';
include 'round.php';
include 'speelveld.php';
include 'player.php';
include 'cells.php';
if(isset($_POST['action']) && !empty($_POST['action'])) {
$action = $_POST['action'];
if (isset($_POST['Val']) && !empty($_POST['Val'])) {
$Val = $_POST['Val'];
}
switch ($action) {
case 'Start' :
echo"<script>alert('new game started')</script>";
$Game = new Game;
break;
case 'ButtonClickStart':
$Game->ButtonClickStart();
break;
case 'ButtonClickStop' :
$Game->ButtonClickStop();
break;
case 'ClickCell' :
$Game->ClickCell( $Val );
break;
// ...etc...
}
}
?>
i call this file trough $ajax and try to make it execute Functions to the Class, however what i cant seem to get past is that : "$Game = new Game();" should only be executed onces and it needs to be remeberd. I understood it could be done trough a: static $game = '' , but php didnt seem to like static in combination with a new class.
At first i tried to declare $Game below the includes, however that lead to it executing that everytime upon calling the file trough Ajax, setting $Game to the construction value's.
now what i want cant figure out is, is there a way to only execute $Game = new Game once while getting remeberd (so it doesnt lose the data after the function is done) And being able to use the var in the other cases
Case start gets activated by the index on a onload function, but i it doesnt seem to remeber the data
also as last note im very new to php in general so any info is very welcome
Your problem is not that your Game variable lives globally throughout your execution context (which is very easy, just declare $Game at the beginning of the script, and use it as you're currently doing), but rather you must be able to save the Game object across the users' session.
You need to be able to serialize the Game object somehow and store it in a database, a cookie or somewhere else, and preferrably not in session variables, because, depending on the size of the Game object and the number of users, you could run out of resources quickly.
You could try to put the instance variable in a session variable so it could be accessible from everywhere.
something like this :
session_start();
if(!isset($_SESSION['game'])) $_SESSION['game'] = new Game();
$Game = $_SESSION['game'];
As said by bfhcf, you can store the instance of a class in a session. This is not very ideal (but a potential solution) as using global variables is considered a bad practice.
If you want to make it global across everything, ie classes, you can do as follows:
session_start(); // makes sure you can set the session
// do all other checks ie if(isset....)
$Game = new Game(); // create a new instance of Game class
$_SESSION['game'] = serialize($Game); // store the Game class in a session
Now the class is stored in a session you can access this session anywhere you need.
Now you can access "Game" object from other files, as follows:
// see if a game class already been set
if (isset($_SESSION['game'])
$Game = unserialize( $_SESSION['game'] ); // get the Game class back from the session
else
echo 'No previous instances of Game class found';
To delete the saved game object (session) you can do as follows:
unset($_SESSION['game']);
I have created a USSD application in PHP, Since USSD requests are unique ( new request for each selection ) I need a way to track them , Track in the sence i need a way to store the flow of the application, In my USSD app there are Static menus as well as Dynamic menus ( Retrieved from the Database ) , My problem is , I have created the app with lots of if, else and switch statements, And It's really difficult to update the menu items. So can anyone suggest me a good algorithm to create this USSD app? I mean a efficient way to tackle the problem, I need a way where I could be able to add and remove menus according the clients request, When I do that , my code shouldn't break. Can anyone help me on this?
-Thanks & Regards
PS: I have read this and this and it doesn't actually answer my question.
The best method working with USSD is making each command a separate class. This way you can work , debug and maintain a large USSD application
Example
$ussd = new USSDMessage(); // USSD Class
$main = new Command("menu"); // Starting Command
// Register multiple command
$main->register(new Command("help"));
$main->register(new Command("account"));
$main->register(new Command("transfer"));
$main->register( new Recharge("reacharge"));
$main->run($ussd); // Run Request
Basic Class Structure
// Class To receve message from USSD
class USSDMessage {
function next();
function saveSession();
}
// Basic Command Class
class Command {
private $name;
private $cmd = array();
function __construct($name) {
$this->name = $name;
}
function register(Command $menu) {
$this->cmd[$menu->getName] = $menu;
}
function run(USSDMessage $ussd) {
$key = $ussd->next(); // get next inpur from ussd
if (isset($this->cmd[$key])){
$ussd->run($ussd);
}
}
}
// Recharge Command Example
class Recharge extends Command {
function run(USSDMessage $ussd) {
$key = $ussd->next();
switch ($key){
case "1":
// Proccess card recharge
// Do Somthing
$ussd->saveSession(); // End Session
break;
case "2":
// Process direct deposit
// Do Somthing
$ussd->saveSession(); // End Session
break;
default:
// Format Not supported
// Do Somthing
$ussd->saveSession(); // End Session
break;
}
}
}
Based on what I got from OP's question, here is some hint:
This problem is related to tree traversal. Each state of the menu will be store at one node of the tree.
If you don't have any knowledge about Suffix tree/trie, read here. The idea is simple, as there is a limited option for user to input: 0,1,2,3,4,5,6...9,#,*, we can represent all the states in one tree.
Each node of the tree will be something like:
Node{
Node [] next = new Node[12];//All options from 0 to *
Menu menu;
}
So instead of if and switch, we can easily locate and store all states by using and traversing through the tree from root to leaf. And to refer to all the states, we only need to store one variable: Node root
For example, if we have a series of action #111#. (Assume that # is at index 10 and * at index 11) We will always begin at root -> go to Node at index 10-> go to Node at index 1 ... and finally, return the menu.
Pseudo code:
Menu traverse(String input){
Node cur = root;
for(int index in input){
cur = cur.next[index];
}
return cur.menu;
}
For deleting a state/branch, just traverse from root to that leaf, and replace that leaf node in the parent next array with a null.
Note: You can manage list of menu even more easily if you store all menus in an array Menu[]menu, and for each node, instead of return a Menu, you will return an integer indicates the index of desired menu in the array menu.
You should try to use your DB queries to maintain the state of the application, using the DB queries will help you track the users' progress and also graduate them to the upper levels and demote them to lower levels, that way you can make your life easier in maintaining your code. Take a look at a code snippet below to help you understand session based USSD progress using AfricasTalking platform.
if($userResponse==""){
// this is when you receive nothing from the user. serve them the registration menu
switch ($level) {
case 0:
//. Graduate the user to the next level, so you dont serve them the same menu
$sql1 = "INSERT INTO `some_seesion_name`(`session_id`, `yourIdentifier`,`level`) VALUES('".$sessionId."','".$yourIdentifier."', 1)";
$db->query($sql1);
// Insert the Identifier, since it comes with the first POST
$sql1a = "INSERT INTO YourDB(`yourIdentifier`) VALUES ('".$yourIdentifier."')";
$db->query($sql1a);
// Serve the menu request for name
$response = "CON Please enter your name";
// Print the response onto the page so that our gateway can read it
header('Content-type: text/plain');
echo $response;
break;
// continue the flow of the app ......
$object_cin = new CIO( );
$object_cin->invoke( $_POST[ 'model' ] );
class CIO
{
private function invoke( $model )
{
switch( $model )
{
case 'user_try':
$model = new MUserTry();
$this->send( $model->invoke( 1 ) );
break;
case 'user_new':
$model = new MUserNew( new IUniversals() , new IText(), new IMessage() );
$this->send( $model->invoke() );
break;
case 'user_exist':
$model = new MUserExist( new IUniversals() , new IText(), new IMessage() );
$this->send( $model->invoke() );
break;
case 'tweet':
$model = new MTweet( new IUniversals() , new IText(), new IMessage() );
$this->send( $model->invoke() );
break;
default:
echo "Incorrect Ajax Type provided";
}
}
private function send( $string )
{
echo "|P|" . $string;
}
}
Of course it will grow, and become impossible to maintain.
First of all, what is with the generic Control class? A website is supposed to have multiple controllers (I am assuming that it is somehow related to MVC, because that's where the term "controller" comes from), usually 1 controller per one "page" (granularity may change, depending on architectural pattern).
Next problem is the big switch statement. Each of the case's there should be a separate method in a controller. You end up repeating parts of code, just because of the design mistake.
You've run straight into the Fat Controller antipattern.
A controller is meant to be little more than glue logic. It passes the data from the request (GET, POST, WHATEVER) to whatever model knows how to handle it, formats whatever result the model returns and assigns it to the view to render.
At least that's what's supposed to happen.
Far too often, the developer fills the controller with the application logic, leaving the models as little more than CRUD objects for doing database access. This is not the way to develop an MVC application. Models are supposed to be "domain experts". As well as storing the data the application is working with, they are also meant to know what that data means, what is valid behaviour for given sets of data, and so on. This makes the model reusable, loosely coupled and highly coherent. You can use a rich model with many different view/controller combinations without difficultly.
If the application logic is in the controller, then you're stuck using that controller to execute the application logic, or even worse, you'll end up copying and pasting the same code into different controllers.
If the controller is full of application logic, then that's a sure sign that you need to rethink your design and refactor
i did up a minimalistic Command Pattern example in PHP after reading up about it. i have a few questions ...
i'll like to know if what i did is right? or maybe too minimal, thus reducing the point of the command pattern
interface ICommand {
function execute($params);
}
class LoginCommand implements ICommand {
function execute($params) {
echo "Logging in : $params[user] / $params[pass] <br />";
$user = array($params["user"], $params["pass"]);
// faked users data
$users = array(
array("user1", "pass1"),
array("user2", "pass2")
);
if (in_array($user, $users)) {
return true;
} else {
return false;
}
}
}
$loginCommand = new LoginCommand();
// $tries simulate multiple user postbacks with various inputs
$tries = array(
array("user" => "user1", "pass" => "pass1"),
array("user" => "user2", "pass" => "pass1"),
array("user" => "user2", "pass" => "PaSs2")
);
foreach ($tries as $params) {
echo $loginCommand->execute($params) ? " - Login succeeded!" : " - Login FAILED!";
echo " <br />";
}
i am wondering if there is any difference from simply putting this LoginCommand into a simple function say in the Users class?
if LoginCommand is better fit for a class, won't it be better if it were a static class so i can simply call LoginCommand::execute() vs needing to instanciate an object 1st?
The point of the Command Pattern is being able to isolate distinct functionality into an object (the command), so it can be reused across multiple other objects (the commanders). Usually, the Commander also passes a Receiver to the Command, e.g. an object that the command is targeted at. For instance:
$car = new Car;
echo $car->getStatus(); // Dirty as Hell
$carWash = new CarWash;
$carWash->addProgramme('standard',
new CarSimpleWashCommand,
new CarDryCommand,
new CarWaxCommand);
$carWash->wash();
echo $car->getStatus(); // Washed, Dry and Waxed
In the above example, CarWash is the Commander. The Car is the Receiver and the programme are the actual Commands. Of course I could have had a method doStandardWash() in CarWash and made each command a method in CarWash, but that is less extensible. I would have to add a new method and command whenever I wanted to add new programmes. With the command pattern, I can simply pass in new Commands (think Callback) and create new combinations easily:
$carWash->addProgramme('motorwash',
new CarSimpleWashCommand,
new CarMotorWashCommand,
new CarDryCommand,
new CarWaxCommand);
Of course, you could use PHP's closures or functors for this too, but let's stick to OOP for this example. Another thing where the Commands come in handy, is when you have more than one Commander that needs the Command functionality, e.g.
$dude = new Dude;
$dude->assignTask('washMyCarPlease', new CarSimpleWashCommand);
$dude->do('washMyCarPlease', new Car);
If we had hardcoded the washing logic into the CarWash, we would now have to duplicate all code in the Dude. And since a Dude can do many things (because he is human), the list of tasks he can do, will result in a terrible long class.
Often, the Commander itself is also a Command, so you can create a Composite of Commands and stack them into a tree. Commands often provide an Undo method as well.
Now, looking back at your LoginCommand, I'd say it doesn't make much sense to do it this way. You have no Command object (it's the global scope) and your Command has no Receiver. Instead it returns to the Commander (which makes the global scope the Receiver). So your Command does not really operate on the Receiver. It is also unlikely, that you will need the abstraction into an Command, when doing the login is only ever done in one place. In this case, I'd agree the LoginCommand is better placed into an Authentication adapter, maybe with a Strategy pattern:
interface IAuthAdapter { public function authenticate($username, $password); }
class DbAuth implements IAuthAdapter { /* authenticate against database */ }
class MockAuth implements IAuthAdapter { /* for UnitTesting */ }
$service = new AuthService();
$service->setAdapter(new DbAuth);
if( $service->authenticate('JohnDoe', 'thx1183') ) {
echo 'Successfully Logged in';
};
You could do it somewhat more Command-like:
$service = new LoginCommander;
$service->setAdapter(new DbAuth);
$service->authenticate(new User('JohnDoe', 'thx1138'));
if($user->isAuthenticated()) { /* ... */}
You could add the authenticate method to the User of course, but then you would have to set the Database adapter to the User in order to do the authentication, e.g.
$user = new User('JohnDoe', 'thx1138', new DbAuth);
if ( $user->authenticate() ) { /* ... */ }
That would be possible too, but personally, I don't see why a User should have an Authentication adapter. It doesn't sound like something a user should have. A user has the Credentials required by an Authentication adapter, but the not the adapter itself. Passing the adapter to the user's authenticate method would be an option though:
$user = new User('JohnDoe', 'thx1138');
if ( $user->authenticateAgainst($someAuthAdapter) ) { /* ... */ }
Then again, if you are using ActiveRecord, then your user will know about the database anyway and then you could simply dump all the above and write the entire authenticatation code into the user.
As you can see, it boils down to how you are setting up your application. And that brings us to to the most important point: Design Patterns offer solutions to common problems and they let us allow to speak about these without having to define tons of terms first. That's cool, but often, you will have to modify the patterns to make them solve your concrete problem. You can spend hours theorizing about architecture and which patterns to use and you wont have written a single code. Don't think too much about if a pattern is 100% true to the suggested definition. Make sure your problem is solved.