changed object after storage or object-state used - php

Example:
class UserStorage {
public function addUser(User $user) { //saves to db }
}
class User {
public function setName($name);
}
What if I add a user to the user storage and later change that user object? In this case you might argue that user objects only should be stored on __destruct. But sometimes this isn't an option (eg imagine the user is displayed and updated afterwards).

I agree with Peter, the above model seems a little quirky to me and I would recommend against implicit save to the datastore.
Additionally, a pattern to use is something like:
class UserStorage {
$_user;
function addUser(User user, commit = true) {
if (commit) {
// save to db
} else {
// populate your internal instance
$_user = user;
}
}
}
So if you have multiple updates of a User object in the execution of your PHP application, you can use
addUser(user,false)
all the way until the very last call to
addUser(user)
This will alleviate the need for multiple inserts/updates to the DB.
However, your problem of where in the application to decide to finally save to db remains, and is more about logical flow than object representation. It may be helpful to have a end() function in the script that persists all your objects to the db.

Implicit writes to the database are probably a bad idea. That should be an explicit, controlled operation.
Your pattern is a little weird to me, but I think this how you'd want to do it
class UserStorage
{
const ACTION_INSERT = 'INSERT';
const ACTION_UPDATE = 'UDPATE';
public function addUser(User $user)
{
$this->saveUser($user, self::ACTION_INSERT);
}
public function updateUser(User $user)
{
$this->saveUser($user, self::ACTION_UPDATE);
}
protected function saveUser(User $user, $action)
{
switch ($action) {
case self::ACTION_INSERT:
// INSERT query
break;
case self::ACTION_UPDATE:
// UPDATE query
break;
default:
throw new Exception('Unsupported action');
}
}
}
class User
{
public function setName($name)
{
// whatever
}
}
$userStorage = new UserStorage();
$user = new User();
$userStorage->addUser($user);
$user->setName('Peter');
try {
$userStorage->updateUser($user);
} catch (Exception $e) {
echo "There was an error saving this user: " . $e->getMessage();
}
But Personally I'm not crazy about this class design. There are some well-established patterns for this that are less confusing, such as ActiveRecord.

Related

Extended class pulling through NULL - OOP

I have the following class for all my user methods:
class User {
protected $_db,
$_data;
public function __construct($user = null, $findby = 'id') {
$this->_db = DB::getInstance();
if (!$user) {
........
} else {
........
}
}
.......
public function login($username = null, $password = null) {
$user = $this->find($username, 'username');
if ($user) {
$lockdown = new Lockdown;
}
}
public function find($param = null, $method = null) {
if ($param && $method) {
$data = $this->_db->query("SELECT * FROM users ...");
if ($data->count()) {
$this->_data = $data->result();
return true;
}
}
return false;
}
public function data() {
return $this->_data;
}
}
The above is a completely stripped down version of my user class. I also have another class (lockdown) which extends user:
class Lockdown extends User {
public $getAttempts;
public function __construct() {
var_dump($this->data());
die();
}
}
However when i call the lockdown class inside of the login class, even though the data object should contain all the user information, the var_dump() is simply returning NULL.
From my calculations when the login class is called, the find method should set $_data = USER INFO, which should therefore allow the new Lockdown method invoked just after the ($this->find()) to be able to access the same data method.
I am still learning OOP Programming so don't know if there is something i am missing, but i can't seem to understand the reason as to why the Lockdown class returns NULL on the data method when it should inherit it.
You should not put any computation logic inside a constructor. It makes it hard to test. You also cannot return from a constructor.
You structure is a complete disaster. Both because of your abuse of the inheritance and of global state.
It makes no sense for a class to create a new instance of its own child class for retrieving data. This is probably a result of you attempting to for a User class to combine two different responsibilities: persistence and business logic. This constitutes a violation of Single Responsibility Principle, which then manifest in a form of a convoluted call graph.
Also the whole class Lockdown extends User construct makes no sense. The extends keyword in OOP can be translates as "is special case of" (as per LSP). The class for tracing user's login attempts is not a specialized case of "user".
You should have at least 3 separate classes for this: one for handling the "user's behavior" and other for saving/restoring "user's state" (the approach is called "data mapper"). The third one would be for managing the the failed attempts.
I would also highly recommend watching this lecture.
As for global state, instead of using a singleton anti-pattern, you should have passed the database connection as a constructor's dependency to the class, which need to interact with persistence.
As for the code, at a high level, it should probably looks something like this:
$user = new User;
$mapper = new UserMapper($db);
$user->setName($username)
if ($mapper->fetch($user)) {
if ($user->matchPassword($password)) {
// you have logged in
// add some flag in session about it
header('Location: /greetings');
exit;
}
// check the failed attempts
} else {
// no matching username
}

Singleton PHP on multiple controllers

I created one class (ParseDAO.php) and made it singleton with this method:
public static function getInstance(){
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
Then I created 2 controllers. One to login stuff (LoginController.php) and another do dashboard stuff (DashboardController.php)
In LoginController.php I use this code and it works perfectly:
$instance = ParseDAO::getInstance();
$loginResponse = $instance->loginParse($request->get('userName'), $request->get('password'));
if($loginResponse == true){
return redirect()->route('dashboard');
}
else {
return view('login.erroLogin');
}
At DashboardController I have this code:
$instance = ParseDAO::getInstance();
$userId = $instance->getUserId();
This second line just returns the objectId form parse. If I put this line on loginController.php it returns the correct Id, but at DashboardController.php (where I need this data) nothing returns.
It's like another instance was created even using singleton.
Anybody knows how to solve this problem?
Follow the code of loginParse and getUserId:
/**
* Method login
*/
public function loginParse($username, $password){
if($username != null && $password != null){
try {
self::$user = ParseUser::logIn($username, $password);
return true;
}catch (ParseException $error){
return false;
}
}
else{
return false;
}
}
/**
* #return getUserId
*/
public function getUserId(){
return self::$user->getObjectId();
}
And this is the constructor code (with the true keys):
public function __construct()
{
ParseClient::initialize('xxx','xxx','xxx');
self::$user = new ParseUser();
}
Singletons allow for code from a single execution to use the same instance. On every execution (page load), a new instance is created. Every call to ParseDAO::getInstance() will return the same instance, but only within that execution context.
Without seeing the code in loginParse and getUserId, what is most likely occurring is that you are storing information in the ParseDAO class. That information would be kept during the same execution context, but would disappear on the next execution (because it's a new instance).
You'll have to use Sessions to persist information across multiple executions.
Laravel's Service Container offers a quick and easy way to bind a class as a singleton without the need to implement that pattern yourself. So you can use this to register you singleton with the service container:
app()->bind('ParseDAO', function ($app) {
return new ParseDAO;
});
And then you can use this to access that instance:
$instance = app('ParseDAO');
That will make sure you always get the same instance. You can read more about singletons and the Laravel Service container in the Laravel Documentation.

Using Interfaces in Kohana 3.1.3

I'm trying to build a form wizard in Kohana and am learning a bit as I go. One of the things that I've learn might work best is utilizing a state pattern in my class structure to manage the different steps a user can be in during the form process.
After doing some research, I've been thinking that the best approach may be to use an interface and have all of the steps act as states that implement the interface. After a state validates, it will change a session variable to the next step, which can be read upon the initial load of the interface and call the correct state to use.
Does this approach make sense? If so, how the heck do I make it happen (how do I best structure the filesystem?)
Here is the rough start I've been working on:
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Project_Builder #state
* Step_One #state
* Step_Two #state
**/
interface Project_Builder
{
public function do_this_first();
public function validate();
public function do_this_after();
}
class Step_One implements Project_Builder {
public function __construct
{
parent::__construct();
// Do validation and set a partial variable if valid
}
public function do_this_first()
{
echo 'First thing done';
// This should be used to set the session step variable, validate and add project data, and return the new view body.
$session->set('step', '2');
}
public function do_this_after()
{
throw new LogicException('Have to do the other thing first!');
}
}
class Step_Two implements Project_Builder {
public function do_this_first()
{
throw new LogicException('Already did this first!');
}
public function do_this_after()
{
echo 'Did this after the first!';
return $this;
}
}
class Project implements Project_Builder {
protected $state;
protected $user_step;
protected $project_data
public function __construct()
{
// Check the SESSION for a "step" entry. If it does not find one, it creates it, and sets it to "1".
$session = Session::instance('database');
if ( ! $session->get('step'))
{
$session->set('step', '1');
}
// Get the step that was requested by the client.
$this->user_step = $this->request->param('param1');
// Validate that the step is authorized by the session.
if ($session->get('step') !== $this->user_step)
{
throw new HTTP_Exception_404('You cannot skip a step!');
}
// Check if there is user data posted, and if so, clean it.
if (HTTP_Request::POST == $this->request->method())
{
foreach ($this->request->post() as $name => $value)
{
$this->project_data["$name"] = HTML::chars($value);
}
}
// Trigger the proper state to use based on the authorized session step (should I do this?)
$this->state = new Step_One;
}
public function doThisFirst()
{
$this->state = $this->state->do_this_first();
}
public function doThisAfter()
{
$this->state = $this->state->do_this_after();
}
}
$project = new Project;
try
{
$project->do_this_after(); //throws exception
}
catch(LogicException $e)
{
echo $e->getMessage();
}
$project = new Project;
$project->do_this_first();
$project->validate();
$project->do_this_after();
//$project->update();
Your way certainly looks possible, however I would be tempted to keep it simpler and use some of Kohanas build in features to take care of what you want. For example, I would use Kostache (mustache) and have separate View classes (and potentially templates) for each step. Then the controller becomes quite simple. See the example below (missing session stuff and validation of the step_number). All of the validation is handled in the model. If there is a validation error, an exception can be thrown which can then pass error messages back to the View.
<?php
class Wizard_Controller {
function action_step($step_number = 1)
{
$view = new View_Step('step_' + $step_number);
if ($_POST)
{
try
{
$model = new Model_Steps;
$model->step_number = $step_number;
if ($model->save($_POST))
{
// Go to the next step
$step_number++;
Request::current()->redirect('wizard/step/'.$step_number);
}
}
catch (Some_Kind_Of_Exception $e)
{
$view->post = $_POST;
$view->errors = $e->errors();
}
}
$view->render();
}
}
?>
Hope this makes sense.

Updating Data through Objects

So, lets say I have a record:
$record = new Record();
and lets say I assign some data to that record:
$record->setName("SomeBobJoePerson");
How do I get that into the database. Do I.....
A) Have the module do it.
class Record{
public function __construct(DatabaseConnection $database)
{
$this->database = $database;
}
public function setName($name)
{
$this->database->query("query stuff here");
$this->name = $name;
}
}
B) Run through the modules at the end of the script
class Record{
private $changed = false;
public function __construct(array $data=array())
{
$this->data = $data;
}
public function setName($name)
{
$this->data['name'] = $name;
$this->changed = true;
}
public function isChanged()
{
return $this->changed;
}
public function toArray()
{
return $this->array;
}
}
class Updater
{
public function update(array $records)
{
foreach($records as $record)
{
if($record->isChanged())
{
$this->updateRecord($record->toArray());
}
}
}
public function updateRecord(){ // updates stuff
}
}
A question you could ask yourslef is whether you want to reinvent the wheel or not. ORM layers like Propel or Doctrine already implement object to (R)DBMS mapping, so you might look at their implementation details.
Propel will use your second approach, they even keep flags on a field level to create just one update statement (which will keep database interaction at a minimum). You'll learn a lot if you study their source (or better yet, stop wasting your time and use their implementation - you won't regret it :p).
It depends on how you plan to implement... Doing all the writes at a single point (at the end of a request) is nice because it allows you to optimize your operations by consolidating queries where possible. But to do that you have to create something similar to a UnitOfWork to keep track of whats a delete/update/insert which can open a whole other can of worms.
On the other hand if you do it directly when you call the persistence method on the entity then you dont have to worry about that quite as much.
Both approaches though mean you have to have some way to make sure you always have the current data in your object but the work required to implementation that varies in complexity with he approach you choose.
Example A updates the database whenever setName is called. This function looks like a simple write accessor but it performs expensive actions when called (connecting to the database, executing a query, etc). These unintended site-effects make Example B far more appealing.
As a further example: Later on you might need a Validator class that examines a Record and ensures that the Record is in a valid state. But in order to examine the Record you must define it first by setting a name - so the Record will be persisted before you can validate it's state. Defining object state is not the same as persisting object state.
A data model approach might work better instead of a record-based approach. For instance:
class Model {
protected $_props= array();
public $changed= false;
static public $models= array();
function __set($name, $value) {
$this->changed= true;
$this->_props[$name]= $value;
}
function __construct() {
Model::$models[]= $this;
}
public function save() {
// Execute database query for saving the current Model
}
static public function update() {
foreach (Model::$models as $model) {
if ($model->changed) {
$model->save();
}
}
}
}
A model-based solution really shines when it comes to creating different Model types. For instance:
class Person extends Model {
public function save() {
// Execute person-specific write operations
}
}
class Doctor extends Person {
public function save() {
// Execute all Person write operations
parent::save();
// Save the extra bits that belong to a doctor
}
}
$person1= new Person();
$person->firstname= 'Jon';
$person->lastname= 'Skeet';
$doctor1= new Doctor();
$doctor1->firstname= 'House';
$doctor1->lastname= 'MD';
// Save all modified models
Model::update();
Though I rarely find use for these kind of mass update mechanisms. Write conditions are usually more specific.

How to Design Domain Layer Objects to Represent Multiple Objects & Single Object in Zend Framework?

I'm working on creating a domain layer in Zend Framework that is separate from the data access layer. The Data Access Layer is composed to two main objects, a Table Data Gateway and a Row Data Gateway. As per Bill Karwin's reply to this earlier question I now have the following code for my domain Person object:
class Model_Row_Person
{
protected $_gateway;
public function __construct(Zend_Db_Table_Row $gateway)
{
$this->_gateway = $gateway;
}
public function login($userName, $password)
{
}
public function setPassword($password)
{
}
}
However, this only works with an individual row. I also need to create a domain object that can represent the entire table and (presumably) can be used to iterate through all of the Person's in the table and return the appropriate type of person (admin, buyer, etc) object for use. Basically, I envision something like the following:
class Model_Table_Person implements SeekableIterator, Countable, ArrayAccess
{
protected $_gateway;
public function __construct(Model_DbTable_Person $gateway)
{
$this->_gateway = $gateway;
}
public function current()
{
$current = $this->_gateway->fetchRow($this->_pointer);
return $this->_getUser($current);
}
private function _getUser(Zend_Db_Table_Row $current)
{
switch($current->userType)
{
case 'admin':
return new Model_Row_Administrator($current);
break;
case 'associate':
return new Model_Row_Associate($current);
break;
}
}
}
Is this is good/bad way to handle this particular problem? What improvements or adjustments should I make to the overall design?
Thanks in advance for your comments and criticisms.
I had in mind that you would use the Domain Model class to completely hide the fact that you're using a database table for persistence. So passing a Table object or a Row object should be completely under the covers:
<?php
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();
$db = Zend_Db::factory('mysqli', array('dbname'=>'test',
'username'=>'root', 'password'=>'xxxx'));
Zend_Db_Table_Abstract::setDefaultAdapter($db);
class Table_Person extends Zend_Db_Table_Abstract
{
protected $_name = 'person';
}
class Model_Person
{
/** #var Zend_Db_Table */
protected static $table = null;
/** #var Zend_Db_Table_Row */
protected $person;
public static function init() {
if (self::$table == null) {
self::$table = new Table_Person();
}
}
protected static function factory(Zend_Db_Table_Row $personRow) {
$personClass = 'Model_Person_' . ucfirst($personRow->person_type);
return new $personClass($personRow);
}
public static function get($id) {
self::init();
$personRow = self::$table->find($id)->current();
return self::factory($personRow);
}
public static function getCollection() {
self::init();
$personRowset = self::$table->fetchAll();
$personArray = array();
foreach ($personRowset as $person) {
$personArray[] = self::factory($person);
}
return $personArray;
}
// protected constructor can only be called from this class, e.g. factory()
protected function __construct(Zend_Db_Table_Row $personRow) {
$this->person = $personRow;
}
public function login($password) {
if ($this->person->password_hash ==
hash('sha256', $this->person->password_salt . $password)) {
return true;
} else {
return false;
}
}
public function setPassword($newPassword) {
$this->person->password_hash = hash('sha256',
$this->person->password_salt . $newPassword);
$this->person->save();
}
}
class Model_Person_Admin extends Model_Person { }
class Model_Person_Associate extends Model_Person { }
$person = Model_Person::get(1);
print "Got object of type ".get_class($person)."\n";
$person->setPassword('potrzebie');
$people = Model_Person::getCollection();
print "Got ".count($people)." people objects:\n";
foreach ($people as $i => $person) {
print "\t$i: ".get_class($person)."\n";
}
"I thought static methods were bad
which is why I was trying to create
the table level methods as instance
methods."
I don't buy into any blanket statement that static is always bad, or singletons are always bad, or goto is always bad, or what have you. People who make such unequivocal statements are looking to oversimplify the issues. Use the language tools appropriately and they'll be good to you.
That said, there's often a tradeoff when you choose one language construct, it makes it easier to do some things while it's harder to do other things. People often point to static making it difficult to write unit test code, and also PHP has some annoying deficiencies related to static and subclassing. But there are also advantages, as we see in this code. You have to judge for yourself whether the advantages outweigh the disadvantages, on a case by case basis.
"Would Zend Framework support a Finder
class?"
I don't think that's necessary.
"Is there a particular reason that you
renamed the find method to be get in
the model class?"
I named the method get() just to be distinct from find(). The "getter" paradigm is associated with OO interfaces, while "finders" are traditionally associated with database stuff. We're trying to design the Domain Model to pretend there's no database involved.
"And would you use continue to use the
same logic to implement specific getBy
and getCollectionBy methods?"
I'd resist creating a generic getBy() method, because it's tempting to make it accept a generic SQL expression, and then pass it on to the data access objects verbatim. This couples the usage of our Domain Model to the underlying database representation.

Categories