What is the most elegant way to print breadcrumbs on a website? - breadcrumbs

I've been thinking about this for a long time already. What is the best, or most elegant, way of creating breadcrumbs?
One way would be to have a method be called from controllers. Something like this:
function showUsers()
{
$this->breadcrumbs->add('Users', URL::route('admin.users'));
// ...
}
I personally don't like this way, though, because I'd like to separate the breadcrumb-logic from controllers. Also, if I'd like to e.g. have a Admin Panel -item for many pages inside a controller, I'd need to either define it in the constructor or in every controller method.
Another way could be to utilize named routes, by breaking them into segments. This obviously requires that the routes are sensibly named and structured in some way. Here's some pseudocode:
if($segment[0] == 'admin') {
$breadcrumbs->add('Admin Panel', URL::route('admin');
if($segment[1] == 'users') {
$breadcrumbs->add('Users', URL::route('admin.users');
} elseif($segment[1] == 'foo') {
$breadcrumbs->add(...);
}
}
One issue with this approach is that it's hard to get "data" regarding the current route. For example, I can have a route for showing a single user (admin.users.single), which gets a user ID in the route (e.g. admin/users/{id}). There is no native way for me to construct the correct URL for the breadcrumb item, as the route data is only used inside the controller.
Can you think of any other ways? Opinions on these examples?

To make it more generic instead of having conditional statements on users and user names etc. in the controller. I think it would be best to first define some kind of hierarchy or tree of categories. Storing that info on your backend is up to you (i.e. db, text file, nosql):
+--------------+--------------+---------------------+------------+
| CategoryId | CategoryName | CategoryDisplayName | ParentId |
+--------------+--------------+---------------------+------------+
| 1 | admin | Admin Panel | NULL |
+--------------+--------------+---------------------+------------+
| 2 | admin.users | Users | 1 |
+--------------+--------------+---------------------+------------+
You can separate out the logic of traversing the categories and building of the breadcrumb string. This is easier to understand, test, etc.
i.e. with c#
You can retrieve the list of breadcrumbs based on that data:
IEnumerable<Category> GetHierarchy(string categoryName) {
...
}
i.e. GetHierarchy("Users") would return list "Admin Panel", "Users"
Then build the breadcrumb string:
var currentPage = "admin.users"
strings.Join(">", GetHierarchy(currentPage)
.Select(c => GetLink(c.categoryName, c.categoryDisplayName)))
You can embed this into the view with helpers or just set it from the controller.
GetLink method would return the link containing the route for the category.
i.e.
Users

Related

How do i poulate a field with a parameter from previous page in a multipage form in gravityforms?

I want to build a multipage from.
The first page asks for first name and last name.
I want to greet the user with his first name in the second page.
The best way to do this is to use Live Merge Tags with Populate Anything:
https://gravitywiz.com/documentation/gravity-forms-populate-anything/#live-merge-tags
If you collected the user's first name in a Name field on page 1, you could great him in the field label for a field on page 2 like so:
Hello, #{Name (First):1.3}
(In this example, the field ID for the Name field is 1. The 3 refers to the first name input of a Name field and will always be 3).
If avoiding another plugin (as useful as that one is), you can use either the pre_submission_filter or pre_submission hooks to do this.
If their name was field 1 and lets say the field you'd like to show is field 2...
// THESE FOUR FILTERS WORK TOGETHER TO PRE-POPULATE ALL SORTS OF STUFF, AND YOU CAN ADD TO THIS AS NECESSARY. MINE IS ABOUT 1500 LINES LONG AND IS USED BY SEVERAL FORMS.
add_filter('gform_pre_render', 'populate_forms');
add_filter('gform_pre_validation', 'populate_forms');
add_filter('gform_pre_submission_filter', 'populate_forms', 10);
add_filter('gform_admin_pre_render', 'populate_forms');
function populate_forms($form) {
$form_id = $form['id'];
$current_form = 2; // pretending the form id you are working on is 2.
$future_form = 10; // imaginary form you'll create later for another purpose.
switch($form_id) {
case $current_form:
$first_name = !empty(rgpost('input_1_3')) ? rgpost('input_1_3') : null; // gets the value they entered into the first-name box of field 1.
foreach ($form['fields'] as &$field) {
if ($field->id === '2') { // Make as many of these as necessary.
if ($first_name) { // make sure there's actually a value provided from field 1.
$field->placeholder = $first_name; // not necessary, just habit since sometimes you'd need to have a placeholder to reliably populate some fields.
$field->defaultValue = $first_name; // this is the piece that will actually fill in the value like you'd expect to see in your question.
}
}
}
break;
//case $future_form: do more stuff.
//break;
}
return $form;
}
That should be a decent start for your functionality plugin where you can populate the current and future forms without much hassle. This can also be done with the gform_field_value hook; I've always found the language a bit clumsy with that one, personally.
The plugin mentioned earlier is definitely neat, but I found myself wanting to rely on that stuff less and less.

Searching for TYPO3 Extension: Global Variables

I am searching for a TYPO3 extension that lets me define variables and use them everywhere in the TYPO3 backend (something like %CEO_NAME%).
The customer wants to define names and addresses (for their CEO for example) centrally, so that when another person gets the job they can just change it once and it gets replaced in every RTE, page title, keyword, etc.
Is there some extension that would allow me to do that easily or is there an easy way this could be achieved with TS?
If at all possible I would like to avoid writing an own extension for this as the budget is somewhat tight on that project.
There are some possibilities with typoscript. Which means no editor can maintain the replacements.
One solution:
at last in the rendering process you replace any occurence:
page = PAGE
page {
:
do the rendering of the page
:
// now replace any occurence:
stdWrap.replacement {
1.search = __CEO__
1.replace = John Doe
2.search = __COMPANY__
2.replace = ACME
}
}
Be careful to select an unique key as the replacement is done everywhere (in HTML tags, in (inline) javascript/CSS, ...).
Advantage: this replacement can use regular expressions.
Next solutions:
Enhance the parsefunc, which is used for textarea fields.
tt_content.text.20 { <- example
parseFunc {
constants {
// replaces '###CEO###' with 'John Doe'
CEO = John Doe
}
short {
__CEO__ = John Doe
}
}
}
This will replace the markers only in the fields this parseFunc is active.

How exclude files from the module loader sails.js uses internally

I'm using sails.js with a module i created called def-inc to get some sort of inheritance into controllers and models via mixins. Now i want to store the mixins/traits in a traits folder inside models and controllers. I don't want to pollute the api root with another folder to hold my traits, so the ponint is that if it is possible to exclude a folder or a file, with out having to modify the core files?, or at least a way to override the module-loader and configure it to do this.
This is an example of the path structure i want to use, but without getting extra models/controllers.
.
|-- api
| |-- models
| | |-- traits
| | | |-- accountTraits.js
| | |-- User.coffee
| |-- controllers
| | |-- traits
| | | |-- restfullTraits.js
| | |-- UserController.js
Right now if i do that, i get an extra model called accountTraits (and a table if using mysql adapter).
I've checked the code and documentation, and so far this doesn't seem to be supported atm, but since probably it is a regular pattern (outside sails, rails, laravel, etc) to use other objects that are part of the model domain, but aren't specific db models, i assume that someone have done something similar.
Note: I know that for simplicity i can just move the traits folder to the api root path, and i don't consider traits to be part of services, so please avoid answering that, if it isn't possible, just comment my question.
EDIT:
Based on the code provided by #sgress454, i created this code, just to support loadModels too (Which works in the same way), and have a single fn to modify in case i want to apply the same behavior to other moduleLoader methods. Anyways, i'll leave it here just in case somebody needs it (But be sure to upvote #sgress454 :)
var liftOptions = rc('sails');
// Create the module loader override function
liftOptions.moduleLoaderOverride = function(sails, base) {
// Get a reference to the base loaders methods we want to extend
var baseLoadController = base.loadControllers;
var baseLoadModels = base.loadModels;
// Reusable fn to remove modules that match the defined pattern
var removeTraitsFromAutoLoadModules = function(cb, err, modules){
// Remove all modules whose identity ends with "traits"
modules = _.omit(modules, function(module, identity) {
return identity.match(/traits$/);
});
// Return the rest
return cb(err, modules);
};
return {
loadControllers: function (cb) {
baseLoadController(removeTraitsFromAutoLoadModules.bind(null, cb));
},
loadModels: function(cb) {
baseLoadModels(removeTraitsFromAutoLoadModules.bind(null, cb));
}
};
};
// Start server
sails.lift(liftOptions);
You can override the module loader by passing a moduleLoaderOverride function as an option to sails.lift. The function takes two arguments--a reference to the Sails instance, and an object containing the original module loader methods so that you can still call them. The function should return an object containing methods of the module loader that you'd like to override. For example:
// bottom of app.js
// Get the lift options from the .sailsrc file
var liftOptions = rc('sails');
// Include lodash (you may have to npm install it), or else rewrite
// below without the _.omit call
var _ = require('lodash');
// Create the module loader override function
liftOptions.moduleLoaderOverride = function(sails, base) {
// Get a reference to the base loadControllers method we want to extend
var baseLoadControllers = base.loadControllers;
return {
loadControllers: function (cb) {
// Load all of the controllers
baseLoadControllers(function(err, controllers) {
// Remove all controllers whose identity starts with "traits"
controllers = _.omit(controllers, function(controller, identity) {return identity.match(/^traits/);});
// Return the rest
return cb(err, controllers);
});
}
};
};
// Lift Sails
sails.lift(liftOptions);
You'll have to lift your app with node app.js for this to work--there's no way to put this in a regular configuration file and use sails lift, since those are loaded by the module loader!

MVC Areas and routing

I'd like to have an area called "Products", where I can use routes such as
http://localhost/products/foo
http://localhost/products/bar
I would like to have the views and other assets organized into a folder structure like
/areas/products/views/foo/index.aspx
/areas/products/views/bar/index.aspx
I'd like to keep images, etc specifically related to each product (foo, bar) in their respective /area/products/views/(foo|bar)/ folder.
I also do not want to have to add a controller action for each product.
If I declare a route like
context.MapRoute(
"products-show-product"
, "Products/{id}"
, new { controller = "Products", action = "Index", id=UrlParameter.Optional }
);
and request the url
http://localhost/products/foo
then ProductsController.Index() is called, as I would expect. However, since the view "foo" is not in the views/products or views/shared folder, it isn't being found.
How can I do this so that I can keep each product's pages in a separate folder?
I don't have a concrete answer to your question since I am not sure about my understanding of it. However I have a general feeling for the direction for the solution.
When one starts to change locations of views, the corresponding methods that find those views also need to change. A simple approach would be to override the FindView and FindPartialView methods.
A simple demo. I created an Area called Blog, a Blog controller with an Index method. In my case I user the controller action as the SubFolder but I am sure that this can be extended to your case for each product folder. I assume that the product will be a request argument. Area http://www.freeimagehosting.net/uploads/85b5306402.gif
The basic idea is to interrogate the controllercontext for the controller, area, action and id and modify the what the default viewengine looks for. The default locations for area views looks like "~/Areas/{2}/Views/{1}/{0}.aspx", so we can basically inject values for the view name and in this case ActionName/Index. The view location will end up being ~/Area/Blog/Views/Blog/Index/Index.aspx.
This is just a rough outline, of the code that can be used. The string comparisons can definitely be updated to more robust methods. As it stands this method will work for the entire app as expected, except for the case when a request is made to the Blog area for the Index action.
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (controllerContext.RouteData.DataTokens ["Area"] == "Blog" )
{
if (String.Compare(controllerContext.RouteData.Values ["Action"].ToString(),"Index",true) == 0)
{
var viewLocation = String.Format("{0}/{1}", controllerContext.RouteData.Values["Action"].ToString(), viewName);
return base.FindView(controllerContext, viewLocation , masterName, useCache);
}
}
return base.FindView(controllerContext, viewName, masterName, useCache);
}

CodeIgniter: URIs and Forms

I'm implementing a search box using CodeIgniter, but I'm not sure about how I should pass the search parameters through. I have three parameters: the search string; product category; and the sort order. They're all optional. Currently, I'm sending the parameters through $_POST to a temporary method, which forwards the parameters to the regular URI form. This works fine. I'm using a weird URI format though:
http://site.com/products/search=computer,sort=price,cat=laptop
Does anyone have a better/cleaner format of passing stuff through?
I was thinking of passing it into the products method as arguments, but since the parameters are optional things would get messy. Should I suck it up, and just turn $_GET methods on? Thanks in advance!
Query Strings
You can enable query strings in CodeIgniter to allow a more standard search function.
Config.php
$config['enable_query_strings'] = FALSE;
Once enabled, you can accept the following in your app:
http://site.com/products/search?term=computer&sort=price&cat=laptop
The benefit here is that the user will find it easy to edit the URL to make a quick change to their search, and your search uses common search functionality.
The down side of this approach is that you are going against one of the design decisions of the CodeIgniter development team. However, my personal opinion is that this is OK provided that query strings are not used for the bulk of your content, only for special cases such as search queries.
A much better approach, and the method the CI developers intended, is to add all your search parameters to the URI instead of a query string like so:
http://site.com/products/search/term/computer/sort/price/cat/laptop
You would then parse all the URI segments from the 3rd segment ("term") forward into an array of key => value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Products extends Controller {
...
// From your code I assume you are calling a search method.
function search()
{
// Get search parameters from URI.
// URI Class is initialized by the system automatically.
$data->search_params = $this->uri->uri_to_assoc(3);
...
}
...
}
This would give you easy access to all the search parameters and they could be in any order in the URI, just like a traditional query string.
$data->search_params would now contain an array of your URI segments:
Array
(
[term] => computer
[sort] => price
[cat] => laptop
)
Read more about the URI Class here: http://codeigniter.com/user_guide/libraries/uri.html
If you're using a fixed number of parameters, you can assign a default value to them and send it instead of not sending the parameter at all. For instance
http://site.com/products/search/all/somevalue/all
Next, in the controller you can ignore the parameter if (parameter == 'all'.)
Class Products extends Controller {
...
// From your code I assume that this your structure.
function index ($search = 'all', $sort = 'price', $cat = 'all')
{
if ('all' == $search)
{
// don't use this parameter
}
// or
if ('all' != $cat)
{
// use this parameter
}
...
}
...
}