Switch vs if else - menu

I use if else for custom menus in Wordpress, to load various location menus based on parent page. The agency I work for is adding countless amounts of cities, and it's getting out of hand. One thing I am trying to do, is come up with a more efficient way to check the items, someone suggested switch, and I just wanted to throw this out there and see what you all think. These are not complete codes, and I know the menus are bad UX, and all that, it's not my call. I just want some input on performance differences. thanks.
Here is an example of switch code:
function is_subpage() {
global $post; // load details about this page
if ( is_page() && $post->post_parent ) { // test to see if the page has a parent
return $post->post_parent; // return the ID of the parent post
} else { // there is no parent so ...
return false; // ... the answer to the question is false
}
}
$selectedMenu = "primary";
$my_page_id = is_subpage();
if(!$my_page_id)
$my_page_id = get_the_ID();
switch ($my_page_id) {
case('489'):
$selectedMenu = 'columbus';
break;
case('6583'):
$selectedMenu = 'cumming';
break;
}
wp_nav_menu( array(
'theme_location' => 'main-menu',
'menu' => $selectedMenu,
'menu_class' => 'clearfix'
));
and here is an example of if else code:
if(is_page( '28' ) || '28' == $post->post_parent) { $locationMenu = 'louisville'; }
'menu' => $locationMenu,

Don't second guess or assume anything about the efficiency of an interpreter or compiler. if else might be better at one scenario and switch at another.
The problem with your code is readability and maintainability and not performance. It is hard to be specific without knowing all details about your needs, but it seems like what you need is to have at each post a custom field which indicates the menu associated with that post, and then the admin can configure them and you will have some more coffee time ;)
This is actually a worse solution in terms of performance, but if you really need the site to be fast then you are going to use a caching plugin which will make the whole php related performance discussion just a waste of time.

From a PHP perspective...
In lieu of having the page id to location table in a database, you could include a structure like this on pages you need it:
$idToLocation = array(
"489" => "columbus",
"6583" => "cumming"
// et cetera
);
Then to get the location:
$id = "489"; // for example
if (!array_key_exists($id, $idToLocation)) {
echo "location for id not found";
die();
}
$location = $idToLocation[$id];

Related

Processing an emaillist async in MVC4

I'm trying to make my MVC4-website check to see if people should be alerted with an email because they haven't done something.
I'm having a hard time figuring out how to approach this. I checked if the shared hosting platform would allow me to activate some sort of cronjob, but this is not available.
So now my idea is to perform this check on each page-request, which already seems suboptimal (because of the overhead). But I thought that with using an async it would not be in the way of people just visiting the site.
I first tried to do this in the Application_BeginRequest method in Global.asax, but then it gets called multiple times per page-request, so that didn't work.
Next I found that I can make a Global Filter which executes on OnResultExecuted, which would seemed promising, but still it's no go.
The problem I get there is that I'm using MVCMailer to send the mails, and when I execute it I get the error: {"Value cannot be null.\r\nParameter name: httpContext"}
This probably means that mailer needs the context.
The code I now have in my global filter is the following:
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
HandleEmptyProfileAlerts();
}
private void HandleEmptyProfileAlerts()
{
new Thread(() =>
{
bool active = false;
new UserMailer().AlertFirst("bla#bla.com").Send();
DB db = new DB();
DateTime CutoffDate = DateTime.Now.AddDays(-5);
var ProfilesToAlert = db.UserProfiles.Where(x => x.CreatedOn < CutoffDate && !x.ProfileActive && x.AlertsSent.Where(y => y.AlertType == "First").Count() == 0).ToList();
foreach (UserProfile up in ProfilesToAlert)
{
if (active)
{
new UserMailer().AlertFirst(up.UserName).Send();
up.AlertsSent.Add(new UserAlert { AlertType = "First", DateSent = DateTime.Now, UserProfileID = up.UserId });
}
else
System.Diagnostics.Debug.WriteLine(up.UserName);
}
db.SaveChanges();
}).Start();
}
So my question is, am I going about this the right way, and if so, how can I make sure that MVCMailer gets the right context?
The usual way to do this kind of thing is to have a single background thread that periodically does the checks you're interested in.
You would start the thread from Application_Start(). It's common to use a database to queue and store work items, although it can also be done in memory if it's better for your app.

Paginator (Migration from Cake 1.3 to 2.0)

I am struggling with the paginator in Cakephp 2.0. While I am trying to migrate my application to 2.0 I cant find any solution to jump directly to the last page. In 1.3 it was quiet to do that from outside like this:
echo $this->Html->link(__('Flights'), array('controller' => 'flights',
'action' => 'index','page' => 'last'));
but this little trick putting 'page:last' in does not work anymore in 2.0. Of course there is a Paginator function called last, but this would only help if I would be already inside the app. My Problem is to access from an outside link directly the last page of the paginator.
This is the simple way:
echo $this->Paginator->last('Any text');
Other way to get the number of the last page is:
echo $this->Paginator->counter(array('format' => '{:pages}'));
Then you can use it to generate your link.
For more info:
http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::last
Shortly after creating a bounty for this question I found the solution to MY problem using CakePHP 2.2.4. I was trying to accomplish the same task but instead using version 2.2.4 instead instead of 2.0. Basically if I had a link that looked like http://www.domain.com/articles/page:last that the controller's pagination method would know what page to go to and display the correct results (articles) for that page. For example, if I have 110 articles and the pagination limit is set to 25, by going to that URL it would display page 5 of 5, showing records 101-110. I also wanted the same capability if I go to “page:first”.
I needed to change my library file lib/Cake/Controller/Component/PaginatorComponent.php.
I changed
if (intval($page) < 1) {
$page = 1;
}
To
if ((intval($page) < 1 && $page != "last") || $page == "first") {
$page = 1;
}
I also added
if($page == "last"){
$page = $pageCount;
}
After the line
$pageCount = intval(ceil($count / $limit));
Christian Waschke, with this solution, you can use the same link helper exactly how you wrote it in your question. For me, the link helper looked like this
<?php echo $this->Html->link('Go to Last Page', array('controller' => 'articles', 'action' => 'index', 'page' => 'last')); ?>
You can 'calculate' the last page yourself if 'last' is passed as the page number;
I would discourage making modifications in the CakePHP library files as this will make it hard to perform upgrades in the future.
Basically, the PaginatorHelper uses viewVars that are calculated and set by the PaginatorComponent, as seen here: https://github.com/cakephp/cakephp/blob/master/lib/Cake/Controller/Component/PaginatorComponent.php#L212
You can replicate this in your action; for example:
public function index()
{
if (!empty($this->request->params['named']['page'])) {
switch($this->request->params['named']['page']) {
case 'first':
// replace the 'last' with actual number of the first page
$this->request->params['named']['page'] = 1;
break;
case 'last':
// calculate the last page
$limit = 10; // your limit here
$count = $this->Flight->find('count');
$pageCount = intval(ceil($count / $limit));
// replace the 'last' with actual number of the last page
$this->request->params['named']['page'] = $pageCount;
break;
}
}
// then, paginate as usual
$this->set('data', $this->paginate('Flight'));
}
To improve this, this logic should be moved to a separate method, or to a behavior. However; as seen above, it is not required to make modifications in the PaginatorComponent!
Also note that the 'find(count)' in my example does not take additional conditions, they should be added if required
If you have a look in the CakePHP 1.3 source for paginate(), the code above is comparable; https://github.com/cakephp/cakephp/blob/1.3/cake/libs/controller/controller.php#L1204

How does one get Drupal's current view/page identifier?

What I am looking for is a page_id/view_id that I can use to identify and style specific pages. I would use the title or the url, but there is a chance that it could change if the a higher-up decides that the page should no longer be called Golf, but rather Tee-Time because he likes it better.
Presumably this identifier would not change if the current page were to be a paged view (page 1,2,3,4...).
One way of solving this is the following. It's depending on the url, so if it changes, so does the class-name.
In my themes template.php I implemented hook_preprocess_page:
function mytheme_preprocess_page(&$vars, $hook) {
$body_classes = array();
$body_classes[] = 'page-' . _get_page_name($_SERVER['REQUEST_URI']);
$vars['body_classes'] = implode(' ', $body_classes);
}
function _get_page_name($request_uri) {
static $numeric_subsection = array(
'/node/' => 'node',
);
$preAlias = $request_uri;
$alias = substr(strrchr($preAlias, "/"), 1);
if (strpos($alias, '?') > -1) {
$alias = substr($alias, 0, strpos($alias, '?'));
}
$page_name = $alias;
if (empty($alias)) {
$page_name = 'start';
}
else if (is_numeric($alias)) {
foreach ($numeric_subsection as $section => $pn) {
if (strpos($preAlias, $section) > -1) {
$page_name = $pn;
}
}
}
return $page_name;
}
Then in the main page-template:
<body class="<?php print $body_classes; ?>">
This isn't a generic solution. So you'll probably have to customize this for your specific needs. It will for example need som tweaking to play nicely with path auto.
This depends a little on how your site is put together (panel pages, view pages, "normal" pages). Essentially, you need to figure out what vars are in scope, and then determine which information in them can be used. To determine what is in scope, you can use print_r(array_keys(get_defined_vars())); and then poke around in the individual vars.
An option is to do something in theme_preprocess_page. One option is to get the page data via page_manager_get_current_page(), poke around in there, and then add body classes as needed. Without knowing what you are doing, you essentially need to print_r the results somewhere, look at what you have, and go from there.

Module created page - cached?

This should be a very simple question, but I'm finding it surprisingly hard to find an answer.
I'm creating custom pages using hook_menu that have both static content and dynamic aspects, (mainly from sql queries and views embeds etc...). My question is, how does drupal's cache system treat these custom pages?
I'm concerned because as the traffic ramps up on some occasions, I feel like I need some kind of caching control over the display of these pages, but at this point, I have no idea if they're automatically being cached, or if I need to somehow specify to drupal that I do indeed want them cached.
Just to clarify, these pages are not admin pages, but are accessible by anyone.
The result of a menu callback is cached only when the menu callback returns the output; when the menu callback prints the output, it's not cached.
The code execute to bootstrap Drupal when a page is requested is the following:
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$return = menu_execute_active_handler();
// Menu status constants are integers; page content is a string.
if (is_int($return)) {
switch ($return) {
case MENU_NOT_FOUND:
drupal_not_found();
break;
case MENU_ACCESS_DENIED:
drupal_access_denied();
break;
case MENU_SITE_OFFLINE:
drupal_site_offline();
break;
}
}
elseif (isset($return)) {
// Print any value (including an empty string) except NULL or undefined:
print theme('page', $return);
}
drupal_page_footer();
drupal_page_footer() is the function that caches the result.
function drupal_page_footer() {
if (variable_get('cache', CACHE_DISABLED) != CACHE_DISABLED) {
page_set_cache();
}
module_invoke_all('exit');
}
page_set_cache() is the function that does the real work.
function page_set_cache() {
global $user, $base_root;
if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && page_get_cache(TRUE)) {
// This will fail in some cases, see page_get_cache() for the explanation.
if ($data = ob_get_contents()) {
if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
$data = gzencode($data, 9, FORCE_GZIP);
}
ob_end_flush();
cache_set($base_root . request_uri(), $data, 'cache_page', CACHE_TEMPORARY, drupal_get_headers());
}
}
}
The content is compressed (if the zlib is enabled), and saved in the cache.
If you want the cache the output of a custom menu callback, then you just need to return the output, instead of printing it directly.
function mymodule_callback() {
// …
return $output;
}
Instead of caching the output of the page, you can cache the data the module used to build its output. If, in example, the output data is obtained with an SQL query, you can cache the result of the query.

Magento - get filterable attributes by category

I have created a custom navigation module specifically for a website, but I really want to be able to list filterable attributes by a specific category. So for instance my main navigation is:
Category 1
Category 2
Category 3 etc.
I then that when a user mouses over a category, they are then presented with an expanded menu with a few filterable options e.g.:
Category 1
View by manufacturer:
Manufacturer 1
Manufacturer 2
Manufacturer 3 etc.
I am able to get all filterable attributes for the store, but I want this list to pull in only the filterable attributes per category, as for instance Category 1 may have different manufacturers to Category 2. I then need to cache these results as this will not change often.
The answer that Joe gave was a good starting point, but the attributes didn't returned any options yet. After a lot of frustrations I solved the problem with the following code. Hope it helps all of you out.
$layer = Mage::getModel("catalog/layer");
foreach($categories as $categoryid) {
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
foreach ($attributes as $attribute) {
if ($attribute->getAttributeCode() == 'price') {
$filterBlockName = 'catalog/layer_filter_price';
} elseif ($attribute->getBackendType() == 'decimal') {
$filterBlockName = 'catalog/layer_filter_decimal';
} else {
$filterBlockName = 'catalog/layer_filter_attribute';
}
$result = $this->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
foreach($result->getItems() as $option) {
echo $option->getLabel().'<br/>';
echo $option->getValue();
}
}
The only thing you'll need to do yourself is create the correct link using the getValue() functions.
This code has been tested in Magento 1.5
Magento uses the model Catalog_Model_Layer to accomplish this, so I'm guessing this may be your best bet. Caveat emptor, I have not tested this code yet:
$layer = Mage::getModel("catalog/layer");
foreach($categories as $categoryid) {
$category = Mage::getModel("catalog/category")->load($categoryid);
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();
// do something with your attributes
}
Each iteration here will give you an object of the class Mage_Catalog_Model_Resource_Eav_Mysql4_Attribute_Collection, which you should be able to iterate over in a foreach loop to get your desired output.
For caching, try enabling block caching on your site and give the block a cache tag like the following. Magento will cache the HTML output and all will be right with the world:
protected function _construct() {
$this->addData(array(
'cache_lifetime' => 3600,
'cache_tags' => array(Mage_Catalog_Model_Product::CACHE_TAG),
'cache_key' => $someUniqueIdentifierYouCreate,
));
}
The cache will only be valid for the key you pass, so make sure that, if the menu is to change (w/o flushing the cache, for instance), that the cache key is different.
Hope that helps!
Thanks,
Joe

Resources