Questions about Command Pattern (PHP) - php

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.

Related

DDD - how to deal with get-or-create logic in Application Layer?

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

How do I connect to different databases at run time?

I am making a multi-tenant multi-database app that has one central database and many sub-databases.
The app creates an instance of an organisation in the central database, and to each organisation it creates a sub-database with different tables.
To achieve this, I have made a class class Setup that
Creates the Organisation
Creates its Database
Configures the connection to that database and connects to it
Runs the proper migrations to it.
All wrapped up in a constructor, so upon caling Setup::create all of this runs properly.
Most of the database configuration and connection are inspiried from this tutorial.
To test whether my logic goes as wanted, I interacted with my application via :
Tinker
Web Browser.
To my suprise, the outcome is different in both cases, and never as wanted as far as connecting to another database is concerned.
Interaction with tinker :
After creating calling my Setup::create and having output telling me everything went okay, I try to check for myself what database am I on right now Using:
DB::connection()->getDatabaseName()
It outputs the sub-database name we have just created for the organisation and connected to, which is logical and going accordingly.
However, I attempt to connect to another database by creating a new configuration for it and then connecting to it with the DB methods I have provided, it does not work, I am still on the sub-database I was on.
Interacting with the browser :
This time, having my Setup::create wrapped up properly in my controller's code, I attempt to test everything again, I also made a line in my layout to output me the current database :
<?php echo DB::connection()->getDatabaseName() ?>
At first, while I am still on the central database, its name appears, however after calling Setup::create, it switches to the sub-database -Which is expected- but then, after one refresh, I am on the central database again -Which is totally Unexpected-
So, what happens here? and how do I get to connect to all of my different databases how I wish when I wish?
Extra:
Testing in tinker, I have went to the point where I have commented out the migration code, and left the creation of the database and also the connection to it.
To my suprise, it does not connect to the database.
so I started thinking that the migration code has something to do with connecting to the database, or maybe tinker has different behaviors I completely ingore.
Important:
I have came across threads where solutions using QueryBuilders were mentioned
Please, do not provide such answers because my aim is to switch databases entirely to the point where I can use eloquent model's events with no problem.
By that I mean, I want to be able to use Model::create after having connected to the database instead of DB::connection()->....
Technical details:
I am using Laravel 5 with mysql-server, on Ubuntu Machine.
I stumbled upon this question and it had my answer.
I made a class called DatabaseConnection:
class DatabaseConnection extends Model
{
static $instances=array();
protected $database;
protected $connection;
public function __construct($options = null)
{
// Set the database
$database = $options['database'];
$this->database = $database;
// Figure out the driver and get the default configuration for the driver
$driver = isset($options['driver']) ? $options['driver'] : Config::get("database.default");
$default = Config::get("database.connections.$driver");
// Loop through our default array and update options if we have non-defaults
foreach($default as $item => $value)
{
$default[$item] = isset($options[$item]) ? $options[$item] : $default[$item];
}
$capsule = new Capsule;
$capsule->addConnection($default);
$capsule->setEventDispatcher(new Dispatcher(new Container));
$capsule->setAsGlobal();
$capsule->bootEloquent();
// Create the connection
$this->connection = $capsule->getConnection();
DatabaseConnection::$instances[] = $capsule;
return $this->connection;
}
}
So, whenever I am in a controller that manipulates tables of a sub-database, I simply go this way:
public function RandomActionInMyController()
{
$db_connection = new DatabaseConnection(['database' => 'name_of_db']);
$someModel = new Model/Model::find()..// Basically anything
return myreturnstuff;
}
Extra Bonus:
The use of the static attribute $instances in my DatabaseConnection
boils down to retrieving my latest database connection for ease uses.
For example, if I ever wanted to retrieve it, it would be wrapped in a function such as
function CurrentOrLatestDbConnection()
{
if( !empty(DatabaseConnection::$instances) )
{
return end(DatabaseConnection::$instances)->getConnection()->getDatabaseName();
}
}
Notes :
If you encounter errors such as Unknown class 'Container' or Capsule or anything of that kind, make sure you check the question link I have provided, and use use statements properly.
Concerning upcoming answers :
It seems to me that this database connection lives within the the brackets of the controller's action, so when I proceed to another action that specifies no connection, it returns to the central database automatically.
Which has got me thinking that there must be a way to set the database connection to the sub-database in a 'global' way to the whole function, such as a middleware or something.
I would love to see an answer, implementing such thing.
Update :
I came up with a neater way to do it.
I assume you are on the same ground as me, wanting to change databases conditionally in accordance with each controller... say, each of your controllers requires a different database, just for the sake of the argument.
What we will be using to solve this is `Middlewares.
First, to explain what we are about to do..
We are going to check for the name of the controller (and even action) and then set the proper database we wish to set.
Go to your command-line , type in:
php artisan make:middleware SetDatabaseConnectionMiddleware
To create a middleware with ready boilerplate.
Or, if you like it the hard way, go to your app_name/app/Http/Middleware and create one manually.
Go to your helper methods file( if you already have one, if not, dude make one!)
function getControllerAndActionName()
{
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
list($controller, $action) = explode('#', $controller);
return ['action' => $action, 'controller' => $controller];
}
This will return to you an array with both the action name and controller name, if you want to return restrictidly just the controller's name, feel free to remove 'action' => $action from the code.
Inside of your middleware, it will look this way :
namespace App\Http\Middleware;
use Closure;
use DatabaseConnection;
class SetProperDatabase
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$database_name = '';
$controllerAndActionName = getControllerAndActionName();
$controller_name = $controllerAndActionName['controller'];
$action_name = $controllerAndActionName['action'];
if($controller_name == 'my_controller_nameController')
{
$database_name = 'your_proper_database_name';
}
else
{
$database_name = 'other_db';
}
$database_connection = new DatabaseConnection(['database' => $database_name']);
return $next($request);
}
}
4.Now, that you have created properly your middleware, let us tell your app where to find it and under what name.
Go to your app_name/app/Http/Kernel.php
In your $routeMiddleware variable, add this line
'set_proper_database' => \App\Http\Middleware\SetProperDatabase::class,
This way we know how to call it.
Finally, setting it up.
Go to your Controller.php (the Abstract class from which all of your controller's inherit)
public function __construct()
{
$this->middleware('set_proper_database');
}
And this should do it for you.
If you have any further questions, please feel free to comment.
// Resources :
1.Controller And Action Name
2.Middleware Documentation
3.Further Middleware Documentation
Notes :
I'd appreciate some edition concerning my styling and code indenting, since it seems I struggled to style my code properly in here but in vain, the indentions I used had no effeft.

Design pattern for an authentication system in PHP

I'm learning PHP, and I'm curious if there are elegant solutions to designing an authentication interface for a website.
Basically, I will have three types of users: Regular Users, Admins, and "Power users".
each type of user will have its own actions.
Here is a brief sketch of my current code:
# Regular User = Non-Authenticated
interface IRegularUser {
# Will return an object of a class that implements this interface
public static function loginUser ($name, $pass);
}
# Authenticated User
class AuthUser extends RegularUser {
public function logoutUser();
}
interface IAdmin {..}
interface IPowerUser {...}
class User implements IRegularUser {}
class Admin extends AuthUser implements IAdmin {}
class PowerUser extends AuthUser implements IPowerUser {}
In the script that checks for correct login, I would write something similar to this:
$user = new User();
$user = $user->loginUser($_POST['username'], $_POST['password']);
if (get_class($user) == get_class(new Admin()))
# Redirect to admin_home.html
else if (get_class($user) == get_class(new PowerUser()))
# Redirect to power_user_home.html
I am trying to use the OOP features of PHP, in order to establish some constraints between the entities in my application (Ex: Only by acquiring an User object, you can then login, and acquire an object of type Admin, or PowerUser).
I feel that my authentication system is kind of clunky, and I would like to know if there is a common design pattern for a webapp authentication system.
Actually, its not that easy as it seems at first glance. There are 3 different aspects when dealing with users, that have roles. The "system" you're looking for would consists of these components:
Query adapter
Its responsible for finding records match against some adapter. This can query against almost any type, be it MySQL table, XML file or Mongo DB collection. All your query adapters would implement this interface:
interface QueryAdapter
{
public function recordValid($username, $password);
}
Access Control List (ACL/RBAC)
It must be separated layer, that deals with roles and their permissions. Typically
$roleManager = new RoleManager();
$roleManager->register(array(
'admin' => 'write, edit, read, delete',
'user' => 'view',
'moderator' => 'write, edit, read'
));
$currentUser = get_that_from_session();
if ($roleManager->isAllowed($currentUser, 'write')) {
// Allow writing
} else {
echo 'You are not allowed to write';
}
Storage adapter
That is responsible for storing passwords and tokens inside either $_SESSION or $_COOKIE. If user clicked on Remember me, then you would store that info inside $_COOKIE, otherwise in $_SESSION.
Typical workflow
To "connect" them all together, you would write it like this : (Keep in mind, this is extremely simplified version of it)
<?php
$queryAdapter = new Query_Adapter_MySQL($pdo); // or $mongo, whatever
if ($queryAdapter->rowExists($_POST['username'], $_POST['password'])) {
$role = $queryAdapter->getRole(); // Let's assume that its "user"
$roleManager = new RoleManager();
$roleManager->register(array(
$role => 'read'
));
if (isset($_POST['rememberMe'])){
$storageAdapter = new StorageAdapter_Cookie();
} else {
$storageAdapter = new StorageAdapten_Session();
}
$storageAdapter->write(array(
'role' => $role,
'passwordHash' => $passwordHash,
'token' => $token
));
}
To check if that user logged in, and has a right to do something, you would simply query an adapter to sees that, like,
<?php
if ($storageAdapter->isLoggedIn()){
if ($roleManager->hasRight($storageAdapter->get('role'), 'read')){
// Allowed to read content
}
}
What would you gain with this approach?
Clear separation of responsibilities (This adheres to Single-Responsibility Principle)
Improved code readability
Adhering to Dependency Injection (thus making unit-testing possible)
You can easily switch from MySQL to another storage, without affecting the rest of your code. You would simply inject an instance of the adapter you're going to use.
Is that it?
Yes. You can also take a look at how Zend Framework implements this

What is the best way to represent configuration options internally?

So, I am looking at a number of ways to store my configuration data. I believe I've narrowed it down to 3 ways:
Just a simple variable
$config = array(
"database" => array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
)
);
echo $config['database']['host'];
I think that this is just too mutable, where as the configuration options shouldn't be able to be changed.
A Modified Standard Class
class stdDataClass {
// Holds the Data in a Private Array, so it cannot be changed afterwards.
private $data = array();
public function __construct($data)
{
// ......
$this->data = $data;
// .....
}
// Returns the Requested Key
public function __get($key)
{
return $this->data[$key];
}
// Throws an Error as you cannot change the data.
public function __set($key, $value)
{
throw new Exception("Tried to Set Static Variable");
}
}
$config = new stdStaticClass($config_options);
echo $config->database['host'];
Basically, all it does is encapsulates the above array into an object, and makes sure that the object can not be changed.
Or a Static Class
class AppConfig{
public static function getDatabaseInfo()
{
return array(
"host" => "localhost",
"user" => "root",
"pass" => "",
"database" => "test"
);
}
// .. etc ...
}
$config = AppConfig::getDatabaseInfo();
echo $config['host'];
This provides the ultimate immutability, but it also means that I would have to go in and manually edit the class whenever I wanted to change the data.
Which of the above do you think would be best to store configuration options in? Or is there a better way?
Of those 3 options, a static method is probably the best.
Really, though, "the best" is ultimately about what's easiest and most consistent for you to use. If the rest of your app isn't using any OO code then you might as well go with option #1. If you are ultimately wanting to write a whole db abstraction layer, option #2.
Without knowing something more about what your goals are and what the rest of your app looks like, it's kind of like asking someone what the best motor vehicle is -- it's a different answer depending on whether you're looking for a sports car, a cargo truck, or a motorcycle.
I'd go with whats behind door #3.
It looks easier to read and understand than #2, and seems to meet your needs better than #1.
Take a look at this question for ideas on storing the config data in a separate file:
Fastest way to store easily editable config data in PHP?
I'd use method #2 pulling the config data as an array from an external file.
The best way is that which fits your application best.
For a small app, it might be totally sufficient to use an array, even it is mutable. If no one is there to modify it except you, it doesn't have to be immutable.
The second approach is very flexible. It encapsulates data, but does not know anything about it. You can pass it around freely and consuming classes can take from it what they need. It is generic enough to be reused and it does not couple the config class to the concrete application. You could also use an interface with this or similar classes to allow for type hints in your method signatures to indicate a Config is required. Just don't name it stdDataClass, but name it by it's role: Config.
Your third solution is very concrete. It hardcodes a lot of assumptions about what your application requires into the class and it also makes it the responsibility of the class to know and provide this data through getters and setters. Depending on the amount of components requiring configuration, you might end up with a lot of specific getters. Chances are pretty good you will have to rewrite the entire thing for your next app, just because your next app has different components.
I'd go with the second approach. Also, have a look at Zend_Config, as it meets all your requirements already and let's you init the Config object from XML, Ini and plain arrays.

Symfony Model Callback Equivalent

I'm working on a Symfony project (my first) where I have to retrieve, from my Widget class, a set of widgets that belong to a Page. Before returning the results, though, I need to verify--against an external service--that the user is authorized to view each widget. If not, of course, I need to remove the widget from the result set.
Using CakePHP or Rails, I'd use callbacks, but I haven't found anything similar for Symfony. I see events, but those seem more relevant to controllers/actions if I'm reading things correctly (which is always up for discussion). My fallback solution is to override the various retrieval methods in the WidgetPeer class, divert them through a custom method that does the authorization and modifies the result set appropriately. That feels like massive overkill, though, since I'd have to override every selection method to ensure that authorization was done without future developers having to think about it.
It looks like behaviors could be useful for this (especially since it's conceivable that I might need to authorize other class instances in the future), but I can't find any decent documentation on them to make a qualified evaluation.
Am I missing something? It seems like there must be a better way, but I haven't found it.
First of all, I think behavior-based approach is wrong, since it increases model layer coupling level.
There's sfEventDispatcher::filter() method which allows you to, respectively, filter parameters passed to it.
So, draft code will look like:
<somewhere>/actions.class.php
public function executeBlabla(sfWebRequest $request)
{
//...skip...
$widgets = WidgetPeer::getWidgetsYouNeedTo();
$widgets = $this->getEventDispatcher()->filter(new sfEvent($this, 'widgets.filter'), $widgets));
//...skip...
}
apps/<appname>/config/<appname>Configuration.class.php
//...skip...
public function configure()
{
$this->registerHandlers();
}
public function registerHandlers()
{
$this->getEventDispatcher()->connect('widgets.filter', array('WidgetFilter', 'filter'));
}
//...skip
lib/utility/WidgetFilter.class.php
class WidgetFilter
{
public static function filter(sfEvent $evt, $value)
{
$result = array();
foreach ($value as $v)
{
if (!Something::isWrong($v))
{
$result[] = $v;
}
}
}
}
Hope you got an idea.
Here's some documentation on Symfony 1.2 Propel behaviors: http://www.symfony-project.org/cookbook/1_2/en/behaviors.
Why not just have a 'getAllowedWidgets' method on your Page object that does the checks you're looking for? Something like:
public function getAllowedWidgets($criteria = null, PropelPDO $con = null) {
$widgets = $this->getWidgets($criteria, $con);
$allowed = array();
// get access rights from remote service
foreach($widgets as $widget) {
// widget allowed?
$allowed[] = $widget;
}
return $allowed;
}
However, if you always want this check to be performed when selecting a Page's Widgets then Propel's behaviours are your best bet.
Although, at least in theory, I still think that a behavior is the right approach, I can't find a sufficient level of documentation about their implementation in Symfony 1.4.x to give me a warm and fuzzy that it can be accomplished without a lot of heartburn, if at all. Even looking at Propel's own documentation for behaviors, I see no pre- or post-retrieval hook on which to trigger the action I need to take.
As a result, I took my fallback path. After some source code sifting, though, I realized that it wasn't quite as laborious as I'd first thought. Every retrieval method goes through the BasePeer model's doSelect() method, so I just overrode that one in the customizable Peer model:
static public function doSelect( Criteria $criteria, PropelPDO $con = null ) {
$all_widgets = parent::doSelect( $criteria, $con );
$widgets = array();
foreach ( $widgets as $i => $widget ) {
#if( authorized ) {
# array_push( $widgets, $widget );
#}
}
return $widgets;
}
I haven't wired up the service call for authorization yet, but this appears to work as expected for modifying result sets. When and if I have to provide authorization for additional model instances, I'll have to revisit behaviors to remain DRY, but it looks like this will suffice nicely until then.

Categories