Different data sets per subdomain in Cakephp? - cakephp

I am writing a personal bookmarks application and am looking for a way to have totally different content between subdomains.
I have written the application and it works fine, but have yet to implement multiple collections. I have the following models:
Tag (unused so far)
Bookmark
Subject
Bookmarks are categorized in subjects and I'm planning to allow tagging in the future and see if that helps me manage my bookmarks more easily.
The current problem I have is that I'd like to separate bookmarks as a whole. I want to use subdomains like webdevelopment.bookmarks.local, languages.bookmarks.local, linux.bookmarks.local that work with an entire own set of domains and bookmarks.
I am considering adding a new model called Set (short for "bookmark sets") and defining sets based on the subdomain.
According to that plan I'd have to rewrite all $this->...->find-queries in the entire App to contain the condition "set_id" = $SubdomainBasedSetid".
While it wouldn't be that much work, I was wondering if it could be done smarter, maybe that Cake would only see the relevant bookmark set per subdomain.

well, your solution is right. But instead of using subdomains, you can use prefix, so you don't have check and set yourself. Since you use subdomains, I assume that these sets are fixed or rarely change. So you don't really need a sets table, just use the set name directly in your bookmark record, so you don't have to convert between name and id.

According to that plan I'd have to rewrite all $this->...->find-queries in the entire App to contain the condition "set_id" = $SubdomainBasedSetid".
As all models extend AppModel, it is possible to edit all queries there before they happen (ie. DRY). :)
// app/app_model.php
class AppModel extends Model {
public function beforeFind($queryData) { // old query
// make changes
return $queryData; // new query
}
However, if you don't want this functionality for all models (or even if you do for now), a better place might be a behavior as this allows you to pick and choose where and when it is loaded:
// app/models/behaviors/subdomain.php
class SubdomainBehavior extends ModelBehavior {
protected $_defaults = array('field' => 'Site.subdomain');
public function setup(&$model, $config = array()) {
$this->settings[$model->alias] = array_merge($this->_defaults, $config);
}
public function beforeFind(&$model, $queryData) {
$domain = $_SERVER['SERVER_NAME'];
$subdomain = substr($domain, 0, strpos($domain, "."));
$queryData['conditions'][$this->settings['field']] = $subdomain;
return $queryData;
}
}
// app/app_model.php
class AppModel extends Model {
$actsAs = array('Subdomain' => array('field' => 'Set.slug'));
}
Tag (unused so far)
To save reinventing the wheel, you may want to look at CakeDC's tags and utils plugins (the latter contains SluggableBehavior which will help with generating friendly URL parts, such as the subdomains).

Related

How to save and retrive view when it's needed

My goal is to keep session size as small as possible. (Why?.. it's other topic).
What I have is Phase listener declared in faces-config.xml
<lifecycle>
<phase-listener>mypackage.listener.PhaseListener</phase-listener>
</lifecycle>
I want to save all other views, except the last one(maximum two) , in some memcache. Getting the session map:
Map<String, Object> sessionMap = event.getFacesContext().getExternalContext().getSessionMap();
in beforePhase(PhaseEvent event) method is giving me access to all views. So here I could save all views to the memcache and delete them from the session. The question is where in jsf these views that are still loaded in the browser are requested so that I can refill with this view if it's needed. Is it possible at all? Thank you.
To address the core of your question, implement a ViewHandler, within which you can take control of the RESTORE_VIEW and RENDER_RESPONSE phases/processes. You'll save the view during the RENDER_RESPONSE and selectively restore, during the RESTORE_VIEW phase. Your view handler could look something like the following
public class CustomViewHandlerImpl extends ViewHandlerWrapper{
#Inject ViewStore viewStore; //hypothetical storage for the views. Could be anything, like a ConcurrentHashMap
ViewHandler wrapped;
public CustomViewHandlerImpl(ViewHandler toWrap){
this.wrapped = toWrap;
}
public UIViewRoot restoreView(FacesContext context, String viewId) throws IOException{
//this assumes you've previously saved the view, using the viewId
UIViewRoot theView = viewStore.get(viewId);
if(theView == null){
theView = getWrapped().restoreView(context, viewId);
}
return theView;
}
public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException, FacesException{
viewStore.put(viewToRender.getId(),viewToRender);
getWrapped().renderView(context, viewToRender);
}
}
Simply plug in your custom viewhandler, using
<view-handler>com.you.customs.CustomViewHandlerImpl</view-handler>
Of course, you probably don't want to give this treatment to all your views; you're free to add any conditions to the logic above, to implement conditional view-saving and restoration.
You should also consider other options. It appears that you're conflating issues here. If your true concern is limit the overhead associated with view processing, you should consider
Stateless Views, new with JSF-2.2. The stateless view option allows you to exclude specific pages from the JSF view-saving mechanism, simply by specifying transient="true" on the f:view. Much cleaner than mangling the UIViewRoot by hand. The caveat here is that a stateless view cannot be backed by scopes that depend on state-saving, i.e. #ViewScoped. In a stateless view, the #ViewScoped bean is going to be recreated for every postback. Ajax functionality also suffers in this scenario, because state saving is the backbone of ajax-operations.
Selectively set mark components as transient The transient property is available for all UIComponents, which means, on a per-view basis, you can mark specific components with transient="true", effectively giving you the same benefits as 1) but on a much smaller scope. Without the downside of no ViewScoped
EDIT: For some reason, UIViewRoot#getViewId() is not returning the name of the current view (this might be a bug). Alternatively, you can use
ExternalContext extCtxt = FacesContext.getCurrentInstance().getExternalContext();
String viewName = ((HttpServletRequest)extCtxt.getRequest()).getRequestURI(); //use this id as the key to store your views instead

CakePHP: how do I provide hard coded data for a model instead of a table to provide it?

I presently have a model called UserLevels which defines some user levels (user, premium user, admin, etc) and some properties of them (number, description, color, etc).
I've decided I would rather hard code this, and be able to use the __() method on the names and descriptions.
How do I go about providing data to a model so that it doesn't use a database? Is there a better way to approach this?
Thank you.
Update: Full multi-field records in a model without a database
After the author made his goal more clear, this should be the solution:
How to use models without database on CakePHP and have associations?
Don't get the title wrong, read it first, the answer describes exactly what would be the solution here as well.
Solution for single field, enum like data:
If you don't use a DB table and entries are limited it is always good to use constants because you can't do a typo without causing an error somewhere and UserLevel::USER is much more clear than a random 'user' string somewhere that could mean anything.
class UserLevel extends AppModel {
public $useTable = false;
const ADMIN = 'admin';
const USER = 'user';
/* ... */
public function getUserLevels() {
return [
UserLevel::ADMIN => __('Admin'),
/* ... */
];
}
}

cakePHP - model relations

I'm developing a system for a University here in Brazil. In this sytem I've got the following models:
- Country;
- State;
- City;
- University;
- Unit;
- Class;
Ok. and the relations are:
- State belongs to Country;
- City belongs to State;
- University belongs to Country (because, in my database, not every country has states registered to it and not every state has citys registered to it, so I could not demand from the user to register a University to a City);
- Unit belongsTo university;
- Class belongs to University (because not every University has Units registered to it);
Ok. I guess now I've set a good background for you guys to understand my situation. Now onto the question itself:
In the Views of my Model Class I wanna display the Country and (IF there are State City). And I may have the need to display such content in other Model views such as Unit and University.
But when I try to do that in my Class model I can only display the country_id. The foreign key in my university database table. And why is that, that is because my Class model belongs to University, so it's pretty easy to access my University's properties. However I do not wish to access the id of the Country, I want it's Name. And maybe in the future I might want other properties, who knows?
HOW DO I DO THAT? How do I access the properties of the Model Country when my Model Class has NO direct relation to it?
many thx hugs and kisses. Hope someone can help me with this one.
p.S:
I've managed a kinda loser solution: In my Class Controller I've done the following (so I can access variables of the Models Country, State and City):
In the View function I've loaded the Models for State and City (the country Model I can access by the relation Class->University->Country);
Then I used a find method to find the respective country, state and city for the Class in question I wanna display in view.ctp. The code follows:
public function view($id = null) {
$this->Class->id = $id;
$this->set('class', $this->Class->read());
$this->loadModel('State');
$this->loadModel('City');
$country = $this->Class->Universsity->Country->find('first', array('conditions' => array('Country.id' => $this->Class->data['University']['country_id']),));
$state = $this->State->find('first', array('conditions' => array('State.id' => $this->Class->data['University']['state_id']),));
$city = $this->City->find('first', array('conditions' => array('City.id' => $this->Class->data['University']['city_id']),));
$this->set('country',$country['Country']);$this->set('city',$city);
$this->set('state',$state);
}
And it kinda works...In my Wiew.ctp I get an array for Country data, and array for State data and an array for City data. And in the view I check to see if the state and city arrays are not length = 0 to display them and all...but I think there HAS to be a better way to do This.
P.P.S:
The other question is...what about the Index.ctp? How will I do that if I do not know which Class I'm working with? Will I have to have logic and find methods in the View? Isin't that just messing up the code?
Without posting your code, it's difficult to point you in the right direction. However, working on the assumption that you have your models configured correctly you should be able to do the following:
ClassController.php
public class ClassController extends AppController{
public $uses = array('Class');
public function view($id){
$class = $this->Class->find('first', array('conditions' => array('Class.id' => $id));
$this->set('class', $class);
}
}
view.ctp
<?php echo $class['Country']['name']?>
You may find however you will have to configure the recursive parameter of your class model so that CakePHP retrieves all the associated data. This can be done one of two ways:
Configure the recursive parameter within your Class model (see the documentation on how to do this). This sets the default value for recursive whenever you retrieve records from the database and you don't specify the attribute in your options.
Set the recurisve parameter within your find(...) method calls. I favor this one because it helps avoid placing a heavy load on the database when you don't need the data it retrieves.
For example, in your controller you could do the following:
public function view($id){
$class = $this->Class->find('first', array('conditions' => array('Class.id' => $id), 'recursive' => 3));
$this->set('class', $class);
}
Just a warning, don't set recursive to a value higher than you need, otherwise you may retrieve stuff that you don't particularly require. Use the debug() function to inspect what you're model is currently retrieving so you can make more of an informed decision:
debug($class);
I hope this is of some help!

Testing models with Translate behaviour

I have the following test case in my CakePHP (2.0.1) app:
<?php
public function testGetTenUsers() {
$users = $this->User->find('all' , array('limit' => 10));
// .... assert some things
}
?>
This works fine, and returns 10 records from my fixture.
When I add the Translate behaviour to my User model, this test no longer works (returns empty array).
Not sure how to approach this. Do I create an I18n fixture, or unbind the behaviour?
Any help appreciated.
Like you just stated, the best solution is to create a fixture.
Unbinding this behavior will make your tests less likely to catch errors. My point is: The further you are from your production configuration, the more complex it becomes to find bugs and quirks.
Here is my default fixture for i18n:
<?php
// I'm working in CakePHP 1.3 at the moment
class I18nFixture extends CakeTestFixture {
public $name = 'I18n';
public $table = 'i18n';
public $import = array(
'table'=>'i18n',
'records'=>true
);
}
I imported the rows in my i18n table from the default configuration, because I only have static content in it (i.e. translation for types, roles). I wouldn't recommend doing this if you have many user input stored in it.
Please also note that you have to specify the table name in the fixture if you want to avoid the inflector to kick in. (so your table name stays "i18n" and not "i18ns")

Generating pages from a database

I'm looking for some help understanding how to generate pages from a database to create a catalog of items, each with different URLs. All I can seem to find through google are products that will do this for me, or full e-commerce solutions. I don't want a shopping cart! Just an inventory.
Also, perhaps someone could recommend their favorite/the best simple login solution.
Thank you so much for your time and any help, suggestions, comments, solutions.
I just posted a thorough solution to another question that is very closely-related to this question. I'll re-post it here for your convenience:
I would suggest using some of the MVC (Model, View, Controller) frameworks out there like KohanaPHP. It is essentially this. You're working in a strictly Object-Oriented environment. A simple page in Kohana, build entirely from a class would look like this:
class Home_Controller extends Controller
{
public function index()
{
echo "Hello World";
}
}
You would then access that page by visiting youur url, the class name, and the method name:
http://www.mysite.com/home/ (index() can be called after home/, but it's implicit)
When you start wanting to bring in database-activity, you'll start working with another Class called a Model. This will contain methods to interact with your database, like the following:
class Users_Model extends Model
{
public function count_users()
{
return $this->db->count_records('users');
}
}
Note here that I didn't write my own query. Kohana comes with an intuitive Query Builder.
That method would be called from within your Controller, the first class that we mentioned at the beginning of this solution. That would look like this:
class Home_Controller extends Controller
{
public function index()
{
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
echo "We have " . $userCount . " users!";
}
}
Eventually, you'll want more complicated layouts, which will involve HTML/CSS/Javascript. At this point, you would introduce the "Views," which are just presentation layers. Rather than calling echo or print from within the Controller, you would load up a view (an HTML page, essentially) and pass it some variables:
class Home_Controller extends Controller
{
public function index()
{
$myView = new View("index");
$usersModel = new Users_Model;
$userCount = $usersModel->count_users();
$myView->userCount = $userCount;
$myView->render(TRUE);
}
}
Which would load the following "View"
<p>We have <?php print $userCount; ?> users!</p>
That should be enough to get you started. Using the MVC-style is really clean, and very fun to work with.
There's a lot of tools out there for generating a web interface around a data model. I find Django pretty easy to use. Based on its popularity, I'm sure that Ruby on Rails is another viable option.

Resources