I am building a CMS kind of site, There will be lot of admin users. ACL was in place for business layer. But now we want to apply custom ACL logic to models based on city in which admin user belongs.
For eg:
Admin user is from New York. He can view the content related to New York City.
I have lot of queries built with Zend_Db_Select in the models. Now I have change the queries everywhere. Is there a way, I can add the logic ->where('u.city_id = ?', $admin_user_city_id)
for each and every query.
Thanks in advance.
Thanks
Venu
I think that you might not need to extend Zend_Db_Table_Select. You can do what you're looking for by only extending Zend_Db_Table_Abstract with a My_Db_Table_Abstract that all your models will extend too. In that abstract class, you'll extend the default select() that returns a Zend_Db_Table_Select and, before returning it, you just add your where clause to it.
Thus, everytime you'll call a select with $myModel -> select() it will already contain your where clause.
abstract class My_Db_Table_Abstract extends Zend_Db_Table_Abstract
{
public function select($withFromPart = self::SELECT_WITHOUT_FROM_PART)
{
$select = parent::select($withFromPart);
# Retreive $admin_user_city_id
$select -> where('u.city_id = ?', $admin_user_city_id);
return $select;
}
}
Of course that also implies that you have made the correct join to your u table somewhere depending on the model you're on.
Related
Right now, I'm programming in PHP/Laravel, but I think this might apply to any other MVC framework. I'll use PHP/Laravel syntax.
I have an app that will need a very basic audit trail. The audit table (audit_event) in the databse is something like:
(id, user_id, event_id, description, occured_at)
As you guess, the user_id is the user that caused the event_id. The description is here if you need to be more verbose, occured_at is the timestamp for the event.
Now, I created an AuditEvent model that has the relations to User and Event. I also wrote the relations from User and Event to AuditEvent.
The function to write an audit event is somethin as simple as:
public static function audit($event, $description = "") {
$id = \Auth::user()->id;
$ae = new AuditEvent();
$ae->user_id = $id;
$ae->event_id = $event;
$ae->description = $description;
$ae->save();
return null;
}
My question is... where should this function be? Should it be in the Model, or should I create a Controller AuditEventController and place it there?
When I call the function, I must include
use \App\Controllers\AuditEventController
and call it
AuditEventController::audit(5, "whatever")
I know placing the function in the Model will also work, but... what is the correct thing to do to comply with MVC?
How you go about conforming to an MVC architecture is entirely up to you. Laravel does not provide the Model location explicitly so that developers can decide. With that said, since this is something that is going to be doing a CRUD operation it could go in a Model.
You would run your checks in the controller and if everything passed you'd pass a reference to your audit method inside your model.
You mentioned that you need to include use \App\Controllers\AuditEventController with it, so then I'd say put this in your controller and create the actual method that saves it in your model.
I have built a small PHP MVC framework and just want to clarify the best way to get data from one model into another. For example:
I have a Users_model that contains a method called get_users().
I also have Communications_model that needs to get specific or all user data and as such needs to access the get_users() method from the Users_model.
Is it best practice to:
a) Instantiate the Users_model in a controller and pass the data from the get_users() method into the Communications_model?
b) Instantiate the Users_model inside the Communications_model and run get_users() from there, so it can be accessed directly?
c) Another way?
Many thanks for any help.
It depends of your motive behind this.
If you want effect on result, then using well know library, like Doctrine etc. should be your choice.
If you want to learn design patterns, then you should get read about ActiveRecord or DataMapper + Repository patterns. Then implements both and check out.
If you want your code, this way - ORM should represent relations of data, then you should ask what it more important? If you menage communication (bus, train), then user can be there assigned and getting users from communication is OK. If user have communication (like car), then relation is reversed.
All depends, what is you motive behind this. Using library, like Doctrine, could you help you running you application. If you want learn design patterns, then check out both options to get some experience.
What you call "users model" is a repository. And what you call "communication model" looks like a service.
Your communication service should have the user repository passed in constructor as a dependency.
I honestly think, that a huge part of your confusion is that you try to call all of those things "models". Those classes are not part of the same layer. You migth find this answer to be useful.
All are possible ways but what I usually do is, whenever there is any function that I think would be reused a number of times by a number of objects, I declare it as static.
It would save the effort of playing with object declaration and would be easily accessible by ClassName::function();
Again, it's a design choice, usually objects are declared right there in the controller and used as per the need but just to save declaration of objects again and again I follow the approach of declaring function static.
The simple principle here is using the __construct() (constructor) to build the object with the relevant properties from the Database. The User Model will have a static function (therefore accessible through any scope) to create an array of instanced objects by simply passing the model data through a new self() which returns the instance.
The concept is you end up with an array of User_Model instances each being a build of the Database columns to properties. All that's left is to create the Database Model and the functions to retrieve the columns and data.
class Communications_Model {
private $_all_users;
public function getUsers() {
$this->_all_users = Users_Model::loadAllUsers();
}
}
class Users_Model {
private $_example_property;
public function __construct($user_id) {
$data = SomeDatabaseModel::getConnection()->loadUserFromDatabase((int)$user_id);
$this->_example_property = $data['example_column'];
}
public static function loadAllUsers() {
$users = array();
foreach(SomeDataModel::getConnection()->loadAllUsers() as $data) {
$users[] = new self($data['user_id']);
}
return $users;
}
}
Of course, now, you have a $_all_users; property that has an array of instanced User Models containing the data.
Is there a way to override the default behavior of SS Data Objects such that when I assign a static $table_name property to my DataObject the dev/build does not create a table name with the DO name like it normally does?
For example I have this very small Data Object
<?php
class SalesRep extends DataObject {
private static $table_name = 'tbl_users';
}
I am trying to prevent creation of table salesrep on dev/build and also I would like the ORM to know that when I do a $Model->write(); I'm writing to the table tbl_users instead of table salesrep
This is currently not possible with SilverStripe 3.x. SilverStripe uses the "convention over configuration" principle and the database tables always have the same name as the related DataObject.
However, in SS4, with namespacing, you'll be able to define a tablename in your config. As #bummzack already noted, this is currently in alpha.
However, you might try and overwrite DataObject's getBaseTable(), which method like:
/**
* Get the name of the base table for this object
*/
public function baseTable() {
return 'tbl_users';
}
but i doubt it'll work without problems, cause in other places the baseTable property is - again - generated out of the class names.
This is part of using the ORM that is within SilverStripe and can take some getting used to. I would perhaps look at this in two different ways...
1) If your goal is to present a certain name to the user, but have a different table name then the solution is to use singular_name and plural_name and then you are free to name the DataObject however you wish...
class tbl_users extends DataObject {
private static $singular_name = 'Sales Rep';
private static $plural_name = 'Sales Reps';
...
}
..remember the whole point of the ORM is that the PHP class defines the table and it would make sense to keep the table name the same as you'd like to use in the code.
2) If it absolutely has to be a specific table then you can specify it as an external table/content and one of the following solutions might suit you best... "Save to external Table", "External Content Module" or "External Data Module"
I'm using Doctrine 1.2. I'd like to execute a Doctrine_Query that, instead of returning a Doctrine_Collection will return a class of my choosing. That is, something like
$o = Doctrine_Query::create()
->from('Foo')
->execute();
$o; //instance of Doctrine_Collection
will normally return a generic Doctrine_Collection object. Instead I'd like it to return a Foo_Collection object, which I define elsewhere
class Foo_Collection extends Doctrine_Collection
{
public function soSomethingSpecificToAFooObject()
{
}
}
which will allow me to logically group functionality.
Is this possible? From my reading and poking at the code base this would seem to have something to do with hydrators, but I haven't been able to a manual page or tutorial that covers what I'm after.
Im pretty sure you can just add the following to your Record's setUp or construct methods (the table should be available in either one construct is run before setUp i think though):
$this->_table->setAttribute(Doctrine_Core::ATTR_COLLECTION_CLASS, 'Foo_Collection');
You can also set this globally on the Doctrine_Connection if you needed to extend Doctrine_Collection and use a different class throughout all models.
I'm creating an ORM in PHP, and I've got a class 'ORM' which basically creates an object corresponding to a database table (I'm aiming for similar to/same functionality as an ActiveRecord pattern.) ORM itself extends 'Database', which sets up the database connection.
So, I can call: $c = new Customer();
$c->name = 'John Smith';
$c->save();
The ORM class provides this functionality (sets up the class properties, provides save(), find(), findAll() etc. methods), and Customer extends ORM. However, in the future I may be wanting to add extra public methods to Customer (or any other model I create), so should this be extending ORM or not?
I know I haven't provided much information here, but hopefully this is understandable on a vague explanation, as opposed to posting up 300+ lines of code.
I agree with the other answers here - put the additional methods into a descendant class. I'd also add an asterisk to that though: each time you extend the class with extra methods, think about what you are trying to achieve with the extension, and think about whether or not it can be generalised and worked back into the parent class. For example:
// Customer.class.php
function getByName($name) {
// SELECT * FROM `customer` WHERE `name` = $name
}
// ** this could instead be written as: **
// ORM.class.php
function getByField($field, $value) {
// SELECT * FROM `$this->table` WHERE `$field` = $value
}
You're certainly thinking correctly to put your business logic in a new class outside your 'ORM'. For me, instead simply extending the ORM-class, I'd rather encapsulate it with a new, value object class to provide an additional degree of freedom from your database design to free you up to think of the class as a pure business object.
Nope. You should use composition instead of inheritance. See the following example:
class Customer {
public $name;
public function save() {
$orm = new ORM('customers', 'id'); // table name and primary key
$orm->name = $this->name;
$orm->save();
}
}
And ORM class should not extend Database. Composition again is best suited in this use case.
Yes, place your business logic in a descendant class. This is a very common pattern seen in most Data Access Layers generation frameworks.
You should absolutely extend the ORM class. Different things should be objects of different classes. Customers are very different from Products, and to support both in a single ORM class would be unneeded bloat and completely defeat the purpose of OOP.
Another nice thing to do is to add hooks for before save, after save, etc. These give you more flexibility as your ORM extending classes become more diverse.
Given my limited knowledge of PHP I'm not sure if this is related, but if you're trying to create many business objects this might be an incredibly time consuming process. Perhaps you should consider frameworks such as CakePHP and others like it. This is nice if you're still in the process of creating your business logic.
You're definitely thinking along the right lines with inheritance here.
If you're building an ORM just for the sake of building one (or because you don't like the way others handle things) than go for it, otherwise you might look at a prebuilt ORM that can generate most of your code straight from your database schema. It'll save you boatloads of time. CoughPHP is currently my favorite.
I have solved it like this in my Pork.dbObject. Make sure to check it out and snag some of the braincrunching I already did :P
class Poll extends dbObject // dbObject is my ORM. Poll can extend it so it gets all properties.
{
function __construct($ID=false)
{
$this->__setupDatabase('polls', // db table
array('ID_Poll' => 'ID', // db field => object property
'strPollQuestion' => 'strpollquestion',
'datPublished' => 'datpublished',
'datCloseDate' => 'datclosedate',
'enmClosed' => 'enmclosed',
'enmGoedgekeurd' => 'enmgoedgekeurd'),
'ID_Poll', // primary db key
$ID); // primary key value
$this->addRelation('Pollitem'); //Connect PollItem to Poll 1;1
$this->addRelation('Pollvote', 'PollUser'); // connect pollVote via PollUser (many:many)
}
function Display()
{
// do your displayĆng for poll here:
$pollItems = $this->Find("PollItem"); // find all poll items
$alreadyvoted = $this->Find("PollVote", array("IP"=>$_SERVER['REMOTE_ADDR'])); // find all votes for current ip
}
Note that this way, any database or ORM functionality is abstracted away from the Poll object. It doesn't need to know. Just the setupdatabase to hook up the fields / mappings. and the addRelation to hook up the relations to other dbObjects.
Also, even the dbObject class doesn't know much about SQL. Select / join queries are built by a special QueryBuilder object.