Here are some snippets from an application that uses filters similar to what your app must do. The idea with this app is that each user can be a member of one or more companies. And the application can switch between the context of any of the companies in the system. There are security filters on all tables to only show rows that pertain to the current company.
First part:
I created a function to determine the current companyID:
- Code: Select all
function getCompanyID(){
static $company_id = -1;
if ( $company_id == -1 ){
$user = getUser();
if ( !$user ) return null;
$company_id = @$_GET['--company-id'];
if ( !$company_id ){
$company_id = $_SESSION['--company-id'];
}
if ( !$company_id ){
$res = db_query("select company_id from user_companies where user_id='".$user->val('user_id')."' limit 1");
if ( mysql_num_rows($res) > 0 ){
list($company_id) = mysql_fetch_row($res);
}
@mysql_free_result($res);
}
if ( !$company_id ){
$res = db_query("select company_id from companies limit 1");
if ( mysql_num_rows($res) > 0 ){
list($company_id) = mysql_fetch_row($res);
}
@mysql_free_result($res);
}
if ( $company_id ){
$_SESSION['--company-id'] = $company_id;
}
}
return $company_id;
}
Basically, this allows you to change the current company ID at any time by passing the --company-id GET parameter. It stores this in a session variable. If no parameter is set, it just gets the existing company ID from the session.
I place this function in some external PHP file that is included at the beginning of the index.php file. It just needs to be accessible anywhere I need it.
Next, in the Application delegate class, I add a block definition to add the dropdown list to select the current company:
- Code: Select all
function block__before_header(){
$res = db_query("select company_id, company_name from companies order by company_name");
echo '<div style="height: 30px; text-align:right"><select onchange="window.location.href=this.options[this.selectedIndex].value;">';
$url = Dataface_Application::getInstance()->url('');
$url = preg_replace('/&company_id=[^&]*/','', $url);
while ($row = mysql_fetch_assoc($res) ){
$selected = '';
if ( $row['company_id'] == getCompanyID() ){
$selected = ' selected';
}
echo '<option value="'.htmlspecialchars($url.'&--company-id='.$row['company_id']).'"'.$selected.'>'.htmlspecialchars($row['company_name']).'</option>';
}
echo '</select></div>';
}
Now that we have the ability to track the current company ID, we can do a filter. Note that in this particular application, all of the tables contain a company_id field so that each record is associated with one particular company. This convention made is easy to filter any table based on the company_id field.
Finally, and also in the Application Delegate class, I implement the beforeHandleRequest() method to add the current company id as one of the search parameters in all tables except the companies table:
- Code: Select all
function beforeHandleRequest(){
$app = Dataface_Application::getInstance();
$query =& $app->getQuery();
$table = Dataface_Table::loadTable($query['-table']);
if ( !$_POST and !@$query['company_id'] and $query['-table'] != 'companies'){
if ( $table->hasField('company_id') ){
$query['company_id'] = getCompanyID();
}
}
//print_r($query);
}
We went a step further, for data entry, and made it so that the company_id field would be automatically populated with the current company ID when records are inserted. We added the following beforeInsert() and company_id__default() methods to each table delegate class:
- Code: Select all
<?php
class tables_xxx {
function beforeInsert($record){
if ( !$record->val('company_id') ){
$record->setValue('company_id', getCompanyID());
}
}
function company_id__default(){
return getCompanyID();
}
}
In this solution I don't actually use security filters. Instead I just manipulate the query in the beforeHandleRequest phase. This solution was used in this case because the filtering is not done for security - it is done for usability and flow control. Make sure you know the difference in your own application.
-Steve