In my website I want to allow users to view some of the features by using a guest account. The guest account would see much of the same information as a regular user but some things will be limited.
What would be a good approach, programming wise, to this problem?
I copy paste the exact pages that a user would normally have to different .php files, example: guestProfile.php. Then I manually remove the buttons or information a guest would see.
I add a bunch of checks on my views such as:
if(userIsGuest)
// then hide info.
Another solution.
What makes the most logical sense in terms of software engineering? Please try to quantify your answer instead of using opinion.
Abstraction is your friend. Rather than stuffing your views with conditionals, you should pass your views any sub-views you wish to be included in the output. Your view doesn't need to know the inner workings of these sub-views, and you can swap them out depending on whether the user is a normal user or a guest user.
Here's a simplistic example:
class View {
/** Does some view stuff... */
public function addSubview($name, Subview $subview) {
//store subviews to be used in the template
}
}
/**
* Somewhere in a controller far far away...
*/
class GuestController extends Controller {
/** Does some controllery stuff... */
public function exec() {
//..
//prepare the view
$this->view->addSubview('toolbar', $guestToolbarSubview);
}
}
In that case it is better to make the checks (Solution 2). This stays true to the DRY (Don't Repeat Yourself) principle. It keeps your code less redundant and you are less likely to forget to copy changes to your second version of the code.
Try to keep common code in separate php file and include that file wherever required.
Related
I am trying to develope good code organization habits and work exclusively with OOP in php but I can't seem to wrap my head around something.
Here is a simplified description of what I am working with:
I have all my class files in a folder '/resources/Classes'
I have all my html and javascript in '/public_html' & '/public_html/script respectively'
My question is concerning files that are the actions of forms or AJAX requests. For example 'formAction.php' and 'ajaxURL.php'. These files are not Classes and also do not contain any html or other such GUI.
I have been putting them in a folder 'resources/actions' but my gut tells me something about this is not fully OOP.
Is my usage of these files incorrect if I am trying for complete OOP? if so how can I approach this differently.
Here is an actual file from my project as a concrete example:
//file: getBalance.php
<?php
/**
* This script gets the balance of an account from the server
*/
if (!isset($Database)) {
$Database = require_once "../clear_finance_pkg.php";
}
/** #var User $User */
$User = $Database->getUserByID("1");//TODO: user should be set dynamically
$User->setAccounts($Database->getAccountsByUser($User));
if (isset($arg1)) {
$accountID = $arg1;
foreach ($User->getAccounts() as $Account) {
if ($Account->getId() == $accountID) {
$RbcChequing = RbcAccount::accountToRbcAccount($Account, "Chequing");
echo '$' . Money::toDollars($RbcChequing->getBalance());
break;
}
}
} else throw new Exception('Account ID is not set. Could not get balance');
It's difficult to say if your code is complete OOP, but i think it isn't. It looks like you are on the right track, because you are using classes, objects and methods. That's the basic of OOP. No longer large if/else statements and a lot of variables that doesn't make sense, but objects and methods without code-duplication.
I think your question in more related to the seperation of logic and view. First of all. In general it's ok to make a file for a function, but in a large application you will loose the overview. What you are doing know is combine view-related and logic-related things in one file, but actually that's not what you want. The ideal situation is full seperation of logic and view so you can create multiple seperate views based on the same logic.
My advice is to take a look at the MVC-pattern. Take a look at this link. It will help you to get a basic understanding of the MVC-pattern. Notice that you won't longer need to have a file for each function. And you have a seperation of your logic and view elements because you can use the same model in multiple views (although this is maybe not the best example).
Situation: only main page is accessible by default, all other pages needs a logged in user. When a module is loaded without user, a login template should be displayed, and no module. In other words, the $sf_content must be emptied in layout.php which is not 100% ok since there is logic in the layout. Is there elegant way for that? I dont think a helper is OK either....
Check out security filters, this is one standard way security is designed in symfony.
You even can implement your own SecurityFilter class with the functionality you want.
http://symfony.com/legacy/doc/reference/1_4/en/12-Filters#chapter_12_security
It is done by default for you by the sfBasicSecurityFilter filter. You just need a good configuration. Read this part of the Jobeet tutorial. You should use sfDoctrineGuardPlugin (or sfGuardPlugin if you using propell) for user authentication.
To complete my comments above: There are different ways to override the layout. You could use the methods:
setLayout($name)
//or using foward, which forwards current action to a new one (without browser redirection)
forward($module, $action);
inside your action class. In case you wand to modify the layout inside a filter, you can use something simular to this:
class yourFilter extends sfFilter {
public function execute($filterChain) {
if($yourConditionForOverrideTheDefaultLayout) {
//here the syntax to change the layout from the filer
$actionStack = $this->getContext()->getActionStack();
$actionStack->getFirstEntry()->getActionInstance()->setLayout('yourLayout');
}
$filterChain->execute();
}
}
To avoid unnecessary duplication in the layout file you can work with Fragments and Partials.
I am currently involved in the development of a larger webapplication written in PHP and based upon a MVC-framework sharing a wide range of similarities with the Zend Framework in terms of architecture.
When the user has logged in I have a place that is supposed to display the balance of the current users virtual points. This display needs to be on every page across every single controller.
Where do you put code for fetching sidewide modeldata, that isn't controller specific but needs to go in the sitewide layout on every pageview, independently of the current controller? How would the MVC or ZF-heads do this? And how about the rest of you?
I thought about loading the balance when the user logs in and storing it in the session, but as the balance is frequently altered this doesn't seem right - it needs to be checked and updated pretty much on every page load. I also thought about doing it by adding the fetching routine to every controller, but that didn't seem right either as it would result in code-duplication.
Well, you're right, having routines to every controller would be a code-duplication and wouldn't make your code reusable.
Unlike suggested in your question comments, I wouldn't go for a a base controller, since base controllers aren't a good practice (in most cases) and Zend Framework implements Action Helpers in order to to avoid them.
If your partial view is site-wide, why don't you just write your own custom View Helper and fetch the data in your model from your view helper? Then you could call this view helper directly from your layout. In my opinion, fetching data through a model from the view doesn't break the MVC design pattern at all, as long as you don't update/edit these data.
You can add your view helpers in /view/helpers/ or in your library (then you would have to register your view helper path too):
class Zend_View_Helper_Balance extends Zend_View_Helper_Abstract
{
public function balance()
{
$html = '';
if (Zend_Auth::getInstance()->hasIdentity()) {
// pull data from your model
$html .= ...;
}
return $html;
}
}
Note that you view helper could also call a partial view (render(), partial(), partialLoop()) if you need to format your code in a specific way.
This is a pretty simple example, but to me it's enough is your case. If you want to have more control on these data and be able to modify it (or not) depending on a particular view (or controller), then I recommend you to take a look on Placeholders. Zend has a really good example about them here on the online documentation.
More information about custom view helpers here.
When you perform such a task, consider using the Zend_Cache component too, so you won't have to query the database after each request but let's say, every minute (depending on your needs).
What you are looking for is Zend_Registry. This is the component you should use when you think you need some form of global variable. If you need this on EVERY page, then you are best adding it to your bootstrap, if you only need it in certain places add it in init method of relavent controllers.
application/Bootstrap.php
public _initUserBalance()
{
$userId = Zend_Auth::getInstance()->getIdentity()->userId;
$user = UserService::getUser($userId);
Zend_Registry::set('balance', $user->getBalance());
}
application/layouts/default.phtml
echo 'Balance = ' . Zend_Registry::get('balance');
That wee snippet should give you the right idea!
In this case, I usually go with a front controller plugin with a dispatchLoopShutdown() hook that performs the required data access and adds the data to the view/layout. The layout script then renders that data.
More details available on request.
[UPDATE]
Suppose you wanted to display inside your layout the last X news items from your db (or web service or an RSS feed), independent of which controller was requested.
Your front-controller plugin could look something like this in application/plugins/SidebarNews.php:
class My_Plugin_SidebarNews
{
public function dispatchLoopShutdown()
{
$front = Zend_Controller_Front::getInstance();
$view = $front->getParam('bootstrap')->getResource('view');
$view->sidebarNews = $this->getNewsItems();
}
protected function getNewsItems()
{
// Access your datasource (db, web service, RSS feed, etc)
// and return an iterable collection of news items
}
}
Make sure you register your plugin with the front controller, typically in application/configs/application.ini:
resource.frontController.plugins.sidebarNews = "My_Plugin_SidebarNews"
Then in your layout, just render as usual, perhaps in application/layouts/scripts/layout.phtml:
<?php if (isset($this->sidebarNews) && is_array($this->sidebarNews) && count($this->sidebarNews) > 0): ?>
<div id="sidebarNews">
<?php foreach ($this->sidebarNews as $newsItem): ?>
<div class="sidebarNewsItem">
<h3><?= $this->escape($newsItem['headline']) ?></h3>
<p><?= $this->escape($newsItem['blurb']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
See what I mean?
I'm using the term "partial" to refer to a small section of presentational code which is repeated on many views. For example, a sidebar. In vanilla PHP, where the business and presentation logic is mixed, including a sidebar is no trouble:
if($someCondition) {
include('sidebar.php');
}
However, in an MVC design pattern, the presentational logic must be kept in the view whilst the business logic must be kept in the controller. If I wish to include a partial unconditionally, then this is unproblematic since I can just have include('sidebar.php') in my view. However, I can no longer do so conditionally because that if logic is banned from my view.
I have attempted a number of solutions but they all have problems. I am currently using Solution 2:
Solution 1
Create an include function in my view class which could conditionally include content from my controller. So in my controller I could have the following logic:
if($someCondition) {
$this->view->include('sidebar.php');
}
$this->view->show('index.php');
Problems: sidebar.php will need to be included into index.php at a specific point requiring the include method on the view object to do some sort of parsing.
Solution 2
Move control of the partials out of the view and put them into the controller:
if($someCondition) {
$this->view->show('header.php', 'sidebar.php', 'index.php', 'footer.php');
}
else {
$this->view->show('header.php', 'index.php', 'footer.php');
}
Problems: Moves a large portion of the presentational logic into the realm of the controller. It seems to be more natural to me for the view to decide whether or not to include the header. Indeed, every PHP MVC tutorial I can find, has partials under the control of the view and not the controller.
Solution 3
Duplicate the view and alter the clone so that it includes the sidebar. Then I could conditionally load one or the other in the controller:
if($someCondition) {
$this->view->show('indexWithSidebar.php');
}
else {
$this->view->show('index.php');
}
Problems: Duplication of code. Consider what would happen if I had 2 sidebars which I needed to be conditionally loaded. Then I would need index.php, indexWithSidebar1.php, indexWithSidebar2.php, indexWithSidebar1And2.php. This only gets worse with every condition. Remember that the entire point of taking the sidebar out as a partial was to avoid replicating it anyway and this approach seems to defeat the point.
Are any of these solutions the "right" solution and if so, how can I overcome their problems? Is there a better approach out there?
However, in an MVC design pattern, the
presentational logic must be kept in
the view whilst the business logic
must be kept in the controller.
IMHO: From an architecture standpoint, I push my business logic further back, out of the controller. We use services to handle all the business logic and repositories for data retrieval. The services call the repositories and then pass back our data model with all the business logic decided for us. Any logic outside that is really UI logic (show this, hide that), as our returned data could be (should be able to be) used in any kind of application, whether it's a mobile app, windows app, or web app.
You could use an extension helper method for your control, and in the model for the partial you can return EmptyResult() if you don't wish to render the sidebar. Or, more succintly:
<% Html.RenderAction<MyController>(x => x.Sidebar({params})); %>
And then in the controller:
public ViewResult Sidebar({params})
{
SidebarModel model = new SidebarModel();
//...get/build model
if ({someCondition})
{
return View("MySidebarPartialView", model);
}
return new EmptyResult();
}
Have your controller evaluate the condition and pass the result to your view. Then, the view can decide whether to include the partial.
For example, the controller can check whether a variable, $foo, isn't null. It passes the result of the comparison to the view via the model's property, $model->isFooed. In this case, the view can display the sidebar based on the value of $model->isFooed.
I'm about to write a admin panel for my CMS written in CodeIgniter. There will be some user information visible at all time - both in the layout's header section and the sidebar. I'm used to do it in a way that I personally hope and think could be done a lot easier, since I'm tired of sending the same parametres to the view over and over again, when it's dynamic data that needs to be displayed on every page anyways (such as unread messages, username, name, status, etc).
I'll need controllers and models, I know that, but do I have to pass, just for an example, the user's username, unread messages etc. every time I need to load a view? Should I do some kind of library for this?
Now my question is: How would I do it when it comes to best practice and for making it easy to maintain in the future?
I hope my question is understandable :)
Personally, I would extend the Controller library (create a MY_Controller by following the guidance at the bottom of Creating Libraries at codeigniter.com).
You would use your model etc as normal. Then you would create a private function in your MY_Controller class to get the relevant "global" data and call
$this->load->vars('everywhere_data', $data_from_relevant_models);
which would make the data available to all views called from that point on as $everywhere_data. Then add a reference to that function in the constructor of MY_Controller, perhaps with a conditional checking for the user to be actually logged in.
If it's complex to collect and get all that data, you might write a library to handle it for you, but the 'controller' part would still be done by MY_Controller: i.e. to get the data and then use load->vars() to publish it to the view.
As a quick and untested example, MY_Controller would start something like as follows:
<?php
class MY_Controller extends Controller
{
private $logged_in_user;
function MY_Controller()
{
parent::Controller();
if( $this->_logged_in_userid() > 0 )
{
$this->logged_in_user = $this->_get_user( $this->logged_in_userid() );
$this->load->vars('logged_in_username', $this->logged_in_user->username );
} else {
$this->logged_in_user = false;
}
}
...
}
Note that things like _logged_in_userid() would access the session for you (e.g. return $this->session->userdata('logged_in_userid');), and _get_user() would access the relevant models.
Finally, you would have a view that accesses $logged_in_username (or everywhere_data in my first example) which you would call into your headers etc. This leaves your normal controllers uncluttered so that they can focus on delivering their specific functionality, stops you rewriting your code several times AND maintains the MVC ideals.
You could create a View just to hold the information and get it from a $_SESSION variable in the View itself if you want to keep it all in one place.