I have a Magento website with the following category structure (CAPITALS letters are CATEGORIES and small letters are products):
ROOT CATEGORY
APPARELS
SHOP BY SIZE
product1
product2
product3
SHOP BY COLLECTION
product4
product5
SHOP BY DESIGN
product6
product7
product8
product9
I want to show my navigation menu as SHOP BY SIZE, SHOP BY COLLECTION and SHOP BY DESIGN. I don't want the navigation to start with APPARELS level. Is there any way to do this?
Note: As per Magento design, ROOT CATEGORY cannot be shown in navigation menu. The navigation menu starts from categories in 2nd level i.e. APPARELS in this case.
Take a look at navigation.php, you could alter the core functionality but by using a module with rewrite (never directly alter a corefile!). I always start there when i need custom navigation functionality.
http://freegento.com/doc/db/d56/_catalog_2_block_2_navigation_8php-source.html
edit, alltough i often use this method, i would advise to avoid rewriting as much as possible, I don't think its possible in this case tough because we are talking about displaying lvl 2 categories as main nav
IF you truly want to use the design Root -> Apparels -> Shop By * you can do this with a single override and modification
config.xml - this is obviously a heavily simplified file, you'll need to provide a helper rewrite for the file.
<?xml version="1.0"?>
<config>
<helpers>
<catalog>
<rewrite>
<category>Namespace_Module_Helper_Catalog_Category</category>
</rewrite>
</catalog>
</helpers>
</config>
Category.php
This assumes you want to use the first child category under your site's root category. In your case it would be "Apparels". This modification takes into account the use of flat or non-flat category tables. There are other options for selecting the ID, one would be a system configuration with the category list as a source thus allowing you to choose your navigations root category directly.
The crux of this file is getting the Parent ID to be the "root category" you want to base the navigation on. Again, for your case the parent ID would be set to that of the "apparels" category.
class Namespace_Module_Helper_Catalog_Category extends Mage_Catalog_Helper_Category {
public function getStoreCategories($sorted=false, $asCollection=false, $toLoad=true)
{
$parent = Mage::app()->getStore()->getRootCategoryId();
$cacheKey = sprintf('%d-%d-%d-%d', $parent, $sorted, $asCollection, $toLoad);
if (isset($this->_storeCategories[$cacheKey])) {
return $this->_storeCategories[$cacheKey];
}
/**
* Check if parent node of the store still exists
*/
$category = Mage::getModel('catalog/category');
/* #var $category Mage_Catalog_Model_Category */
if (!$category->checkId($parent)) {
if ($asCollection) {
return new Varien_Data_Collection();
}
return array();
}
/* Change ian on 1/4/13 at 11:16 AM - Description: Here we capture the id of first child for use as the 'root' */
$category->load($parent);
/** #var $collection Mage_Catalog_Model_Resource_Category_Collection */
$collection = $category->getChildrenCategories();
if (is_array($collection)) {
$category = array_shift($collection); //get the first category in the array. Unknown key.
$parent = $category->getId();
} else {
$parent = $collection->getFirstItem()->getId();
}
$recursionLevel = max(0, (int) Mage::app()->getStore()->getConfig('catalog/navigation/max_depth'));
$storeCategories = $category->getCategories($parent, $recursionLevel, $sorted, $asCollection, $toLoad);
$this->_storeCategories[$cacheKey] = $storeCategories;
return $storeCategories;
}
}
Related
Good Afternoon,
I cannot figure out how to remove the key board divider key after my favicon in the browser tab. Please see the image below.
Note: I am developing with Drupal 8.
Drupal construct, by default, the page title such: [Entity-Title] | [Site Name].
It's seems you don't have a title on this page.
It depends which page(s) you try to change. For an Entity, just set the title field would fix your probleme.
Titles on routes now can be set on various ways, depending on your use case.
Previously (drupal-7) just drupal_set_title() was called in whatever place. The following use cases exist:
Override title tag
You may override the title tag in the head of your HTML document using the HOOK_preprocess_html.
function mymodule_preprocess_html(&$variables) {
// Change the Title
$variables['head_title']['title'] = 'Title';
// Change the Suffix (sitename)
$variables['head_title']['name'] = 'Suffix';
}
Static title
For static titles you set a '_title' on the routing definition:
block.admin_add:
path: '/admin/structure/block/add/{plugin_id}/{theme}'
defaults:
_controller: '\Drupal\block\Controller\BlockAddController::blockAddConfigureForm'
_title: 'Configure block'
requirements:
_permission: 'administer blocks'
Dynamic title
If you write a controller and you need a dynamic title, for example depending on the site configuration, use _title_callback in the route defaults.
mymodule.test:
path: '/mymodule/test'
defaults:
_controller: '\Drupal\mymodule\Controller\TestController::getContent'
_title_callback: '\Drupal\mymodule\Controller\TestController::getTitle'
<?php
class TestController {
/**
* Returns a page title.
*/
public function getTitle() {
// The \Drupal::config() should be injected instead of using static call.
return 'Foo: ' . \Drupal::config()->get('system.site')->get('name');
}
/**
* Returns a page render array.
*/
public function getContent() {
$build = array();
$build['#markup'] = 'Hello Drupal';
return $build;
}
}
Final title override
If you write a controller and you need to override the title from the route, you can return #title in the render array. This should generally to be avoided, since the title for the page when fully rendered could be different from the title in other contexts (like in the breadcrumb).
<?php
class TestController {
/**
* Renders a page with a title.
*
* #return array
* A render array as expected by drupal_render()
*/
public function getContentWithTitle() {
$build = array();
$build['#markup'] = 'Hello Drupal';
// The \Drupal::config() should be injected instead of using static call.
$build['#title'] = 'Foo: ' . Drupal::config()->get('system.site')->get('name');
return $build;
}
}
drupal_set_title() in Drupal 8
As you can see here that drupal_set_title() is deprecated in Drupal 8.
$request = \Drupal::request();
if ($route = $request->attributes->get(\Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_OBJECT)) {
$route->setDefault('_title', 'New Title');
}
I am implementing a default Sitecore-uCommerce SEO friendly URLs mechanics and I run into issues.
I looked into ItemResolver processor of uCommerce and I still do not understand how uCommerce sets the Sitecore Context Item. It seems like it uses an uCommerce Item's Guid for Sitecore.Context.Item. Somehow it is not the case, but I do not see the real Sitecore Item to be set as Context item. And uCommerce items do not have the layout details on them. Or am I wrong?
private ID FindSitecoreIdForProduct(int productId)
{
IRepository<Product> repository = ObjectFactory.Instance.Resolve<IRepository<Product>>();
Product product = repository.Get(productId);
if (product != null)
{
return new ID(product.Guid);
}
return ID.Null;
}
Then it makes
ID iD = this.FindSitecoreIdForProduct(productId);
if (iD == ID.Null)
{
return;
}
Context.Item = Context.Database.GetItem(iD);
I would like it to be a specific Sitecore Item with a rendering showing the product details. The URLs are of type
http://sitename.com/productdetailpage/productname/c-25/p-125
If you could just explain me how uCommerce gets to the real Sitecore Item and sets it as a Context.Item, I guess it will be enough for me.
You are on the right way.
They move the context item to other item. I didn't like how they deal with url, and I need other ProductResolver.
Ucommerce have products in his own db, and they created a dataProvider to bring products into Sitecore.
The Ucommerce products are located in Sitecore under /sitecore/uCommerce/Products .
The shops,category and subcategory are located under /sitecore/uCommerce/Store.
Please check this link to have a clean idea how ucommerce is dealing with url:
http://docs.ucommerce.net/ucommerce/v7.0/sitecore/working-with-nice-urls-in-sitecore.html
I also have the same issue like you and I created a custom ItemResolver.
I defined processor in this way on httpRequestBegin pipeline.
<processor type="NameSpace.ProductResolver, Assembly" patch:instead="processor[#type='UCommerce.Sitecore.Pipelines.ItemResolver, UCommerce.Sitecore']"/>
I created in Sitecore a new template name ProductPage, I create a new item named Product of type ProductPage
My requirements was to have url like : /Shoes/Running/NIKEAIRZOOMPEGASUS33
When you browse to /category/subcategory/productid my productResolver is triggered.
I check if category,subcategory and product exist.
If they exist I set the current category and current product.
SiteContext.Current.CatalogContext.CurrentProduct=current_product;
//you need to check if product is in current category
I set my Context Item to be the Product item
var pathList = args.LocalPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).ToList();
var currentProduct= GetCurrentProduct(pathList);
// in above function I am getting the current product, checking if is correct category and subcategory
if (currentProduct != null)
{
SiteContext.Current.CatalogContext.CurrentProduct = currentProduct;
Sitecore.Context.Item = productItem;
}
I am trying to display a small set of hierarchical data and have found the AdvancedDataGrid so terrible in how it handles layout I am going to try and approach it another way using Spark Lists.
I am getting from a MySQL database an ArrayCollection of Lessons. Each Lesson has a parent Topic (I have included a TopicName field within each Lesson for ease) and I want to display the Lessons after grouping them by their respective Topics.
I could create a hierarchical data structure, possibly by using a GroupingCollection2, and wondered if I could display a Spark List of Topics and within the topicItemRenderer I create display a Spark List of Lessons for the given Topic?
Any thoughts very welcome.
Chris
To create a grouped list of data I replaced my AdvancedDataGrid with a Spark List within a Spark List. It now has a layout which is predictable and sizes properly. In my case I know that I only have to display approximately 20 items in my grouped List so I don't really have any performance issues.
This is how I did it:
General -
I created a new mxml component dedicated to the list of Lessons. I organise my code following a Model, View, Presenter pattern so I created LessonList_View (mxml) and LessonList_Presenter (ActionScript class). I have a Model class (Singleton) which the presenter classes instantiate to put/get data. The Model raises events when properties change informing the presenters, who have eventListeners, of the change so that they can update their local properties of which the views are bound. The Model calls PHP methods in a Amfphp Service to acquire data from the MySQL database.
Prepare Data -
I have a relational MySQL database containing a table of Lessons and a table of Topics. Each Lesson has to have one parent Topic. A Topic will have many Lessons. I am using Amfphp to get a subset of Lessons data. Each Lesson row from the database is mapped to a Flex class giving me an ArrayCollection of strongly typed value objects of Type VoLesson. To make life simpler I included the topicName field in my VoLesson ActionScript class, only the topicId is available within the MySQL table, and included this in my SELECT statement when getting the data. I also sort the data by Topic and then by Lesson here so its ready for the next step.
Next I need to create an Array Collection containing ArrayCollections of Lessons of the same Topic. This way, I figured, I can have a parent Spark List displaying Topics and within the ItemRenderer for each Topic List Item I can have a List of Lessons.
Once my LessonList_Presenter has got an ArrayCollection of VoLessons I iterate through it. A new, temporary, ArrayCollection of Lessons (_topicLessons) is populated with Lessons until the topicName changes whereupon I add the current _topicLessons ArrayCollection of VoLessons into a parent ArrayCollection (courseTopicLessons).
The function is as follows:
private function updateCourseTopicLessons():void {
// Reset courseTopicLessons.
this.courseTopicLessons = new ArrayCollection();
// Create a variable to hold the last topicName.
var _topicName:String = "";
// Create an ArrayCollection to hold all of the Lessons for a single Topic.
var _topicLessons:ArrayCollection = new ArrayCollection();
// Iterate through the courseLessons.
for each(var _lesson:VoLesson in this.courseLessons)
{
// check to see if this lesson has a different topicName.
if (_lesson.topicName != _topicName) {
//trace("Different Topic: " + _lesson.topicName);
// Add the previous _topicLessons into the courseTopicLessons ArrayCollection.
if (_topicLessons.length > 0) {
//trace("Adding _topicLessons " + _topicLessons.length + " to courseTopicLessons");
this.courseTopicLessons.addItemAt(_topicLessons, 0)
}
// This is a new Topic. Reset _topicLessons.
//trace("Reset _topicLessons");
_topicLessons = new ArrayCollection();
// Update _topicName.
_topicName = _lesson.topicName;
}
// Add the Lesson to _topicLessons.
//trace("Add Lesson: " + _lesson.lessonTitle + " to _topicLessons")
_topicLessons.addItemAt(_lesson, 0);
}
// Add the previous _topicLessons into the courseTopicLessons ArrayCollection.
if (_topicLessons.length > 0) {
//trace("Adding final _topicLessons " + _topicLessons.length + " to courseTopicLessons")
this.courseTopicLessons.addItemAt(_topicLessons, 0)
}
//trace(this.courseTopicLessons)
}
I used .addItemAt() to keep the sort order correct.
Views and ItemRenderers -
In my LessonList_View I created the List and set it as follows:
<!-- Lessons List -->
<s:List
id="lessonList"
dataProvider="{presenter.courseTopicLessons}"
itemRenderer="views.LessonListTopicItemRenderer_View"
borderVisible="false"
borderColor="0xff69b4"
preventSelection="true"
contentBackgroundAlpha="0">
<s:layout>
<s:VerticalLayout
useVirtualLayout="false"
requestedMinRowCount="1"
gap="8"
paddingTop="8" paddingBottom="8"/>
</s:layout>
</s:List>
I used the borders when checking everything to see the extents of the Lists.
My data provider is an ArrayCollection of ArrayCollections. I want to display List of Topics and within each Topic List Item I want to display a List of Lessons. To display the Topics I know that each ArrayCollection within the parent ArrayCollection will have at least 1 VoLesson (I hope you're following this!). I can display the topicName value from this item. Here is my code for the Lesson List's ItemRenderer:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:presenters="presenters.*"
width="100%" height="100%"
autoDrawBackground="false">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<presenters:LessonListTopicItemRenderer_Presenter id="presenter"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import models.Globals;
import vo.VoLesson;
override public function set data( value:Object ) : void {
super.data = value;
// Check to see if the data property is null.
if (value== null)
return;
// If the data property is not null.
var _lesson:VoLesson = VoLesson(value[0]);
topicLabel.text = _lesson.topicName;
}
]]>
</fx:Script>
<s:VGroup gap="8" width="100%">
<!-- Divider line between Topics -->
<s:Line id="topicDividerLine" width="100%">
<s:stroke>
<s:SolidColorStroke color="{presenter.selectedActivityColour_Mid}" weight="1" />
</s:stroke>
</s:Line>
<!-- Topic Label -->
<s:Label
id="topicLabel"
styleName="topicStyle"
color="{presenter.selectedActivityColour}"
maxWidth="{presenter.lessonsListTopicColumnWidth}" />
<s:HGroup paddingLeft="{Globals.LESSONS_LIST_TOPIC_COLUMN_WIDTH}">
<s:List
id="lessonList"
dataProvider="{data}"
borderColor="0xadff2f"
itemRenderer="views.LessonListLessonItemRenderer_View"
borderVisible="false"
preventSelection="true">
<s:layout>
<s:VerticalLayout
useVirtualLayout="false"
requestedMinRowCount="1"
gap="16"
paddingTop="8" paddingBottom="8"/>
</s:layout>
</s:List>
</s:HGroup>
</s:VGroup>
The key thing to remember is that the ItemRenderer will be passed only the data for an individual item in the List, in this case an ArrayCollection of VoLesson objects. Within the element I get the topicName for the first item in the ArrayCollection of VoLessons passed in as 'data' and set my Label's text property.
Below the Topic Label I have my List of Lessons which has the same data provider, an ArrayCollection of VoLesson objects for the same Topic. The ItemRenderer for this List is as follows:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:views="views.*"
xmlns:presenters="presenters.*"
height="100%"
autoDrawBackground="false">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<presenters:LessonListLessonItemRenderer_Presenter id="presenter"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
import vo.VoLesson;
override public function set data( value:Object ) : void {
super.data = value;
// Check to see if the data property is null.
if (value== null)
return;
// If the data property is not null.
var _lesson:VoLesson = VoLesson(value);
lessonLabel.text = _lesson.lessonTitle;
}
]]>
</fx:Script>
<s:HGroup gap="8" verticalAlign="middle">
<views:IconLesson_View />
<s:Label
id="lessonLabel"
styleName="lessonStyle"
color="{presenter.textDarkGrey}"/>
</s:HGroup>
Remember that the 'data' object for this ItemRenderer, and there will be one for each item in the List of Lessons, will be a single VoLesson object. In the element I get the lessonTitle property from the VoLesson and set the lessonLabel Label's text property.
Final List
The List appears as follows:
I have spent many days trying to coerce an AdvancedDataGrid to size itself and layout the content properly, it was dreadful. Yesterday I decided to start again and this works so much better. For a simple grouped list I would recommend a similar approach.
Regards
Chris
You could do it that way and it would work, But its not a good idea if you have a lot data. You would have to worry about item renderers. I would probably use a tree and create custom item renderers if need be. May be some code might help.
I have the following content tree structure:
Home
Products
Product A
Product B
Organizations
Org 1
Org 2
Org Config X
Org Config Y
Each Organization beneath Organizations has a field called "Associated Products" which is a multilist. This tells the system which Products go with each Organization. The Org Config data template has a field called "Selected Products". When I add a new Org Config content item (which always lives directly beneath an Organization) I would like to be able to restrict the items that are displayed in the "Selected Products" field (which is a multilist) to only display Products that are already associated with the parent Organization. I am thinking there might be a way to do this with Sitecore Query but I can't figure it out. Any ideas?
With the help of Sitecore I figured it out. Basically you have to create a custom control that inherits from MultilistEx. Then you need to override the DoRender() event. Before you call base.DoRender() you must change the source (this.Source) to use a Sitecore query. Previously I was trying to do it in the OnLoad event. So my code now looks like this:
public class CustomMultiList : MultilistEx
{
private void ExcludeItems()
{
...custom code here that builds a list of Item IDs to exclude from the Multilist source...
...list should look like this "##id != 'some guid' and ##id != 'some guid' and so forth...
...you could also build a list of item ids to include. Any Sitecore query will do...
...you can use this.ItemID to get a reference to the current item that is being edited in the Content Editor...
this.Source = "query:" + this.Source + "/*[" + myListOfItemIdsToExclude + "]";
}
protected override void DoRender(output)
{
this.ExcludeItems();
base.DoRender(output);
}
}
I think you'll probably need to create a custom field for this. Here's some articles related to the subject:
http://www.sitecore.net/unitedkingdom/Community/Best-Practice-Blogs/Martin-Knudsen/Posts/2012/09/Creating-a-custom-Sitecore-Field.aspx
http://gettingtoknowsitecore.blogspot.co.uk/2010/03/custom-fields-part-1.html
http://sitecoreblog.blogspot.co.uk/2012_04_01_archive.html
I need to put a search box within a list of objects as a result of a typical indexSuccess action in Symfony. The goal is simple: filter the list according to a criteria.
I've been reading the Zend Lucene approach in Jobeet tutorial, but it seems like using a sledge-hammer to crack a nut (at least for my requirements).
I'm more interested in the auto-generated admin filter forms but I don't know how to implement it in a frontend.
I could simply pass the search box content to the action and build a custom query, but is there any better way to do this?
EDIT
I forgot to mention that I would like to have a single generic input field instead of an input field for each model attribute.
Thanks!
I'm using this solution, instead of integrating Zend Lucene I manage to use the autogenerated Symonfy's filters. This is the way i'm doing it:
//module/actions.class.php
public function executeIndex(sfWebRequest $request)
{
//set the form filter
$this->searchForm = new EmployeeFormFilter();
//bind it empty to fetch all data
$this->searchForm->bind(array());
//fetch all
$this->employees = $this->searchForm->getQuery()->execute();
...
}
I made a search action which does the search
public function executeSearch(sfWebRequest $request)
{
//create filter
$this->searchForm = new EmployeeFormFilter();
//bind parameter
$fields = $request->getParameter($this->searchForm->getName());
//bind
$this->searchForm->bind($fields);
//set paginator
$this->employees = $this->searchForm->getQuery()->execute();
...
//template
$this->setTemplate("index");
}
It's important that the search form goes to mymodule/search action.
Actually, i'm also using the sfDoctrinePager for paginate setting directly the query that the form generate to get results properly paginated.
If you want to add more fields to the search form check this :)
I finally made a custom form using the default MyModuleForm generated by Symfony
public function executeIndex {
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
but displaying only a custom field:
<div id="search_box">
<input type="text" name="criteria" id="search_box_criteria" value="Search..." />
<?php echo link_to('Search', '#my_module_search?criteria=') ?>
</div>
Then I created a route named #my_module_search linked to the index action:
my_module_search:
url: my_module/search/:criteria
param: { module: my_module, action: index }
requirements: { criteria: .* } # Terms are optional, show all by default
With Javascript (jQuery in this case) I append the text entered to the criteria parameter in the href attribute of the link:
$('#search_box a').click(function(){
$(this).attr('href', $(this).attr('href') + $(this).prev().val());
});
And finally, back to the executeIndex action, I detect if text was entered and add custom filters to the DoctrineQuery object:
public function executeIndex {
...
// Deal with search criteria
if ( $text = $request->getParameter('criteria') ) {
$query = $this->pager->getQuery()
->where("MyTable.name LIKE ?", "%$text%")
->orWhere("MyTable.remarks LIKE ?", "%$text%")
...;
}
$this->pager->setQuery($query);
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
Actually, the code is more complex, because I wrote some partials and some methods in parent classes to reuse code. But this is the best I can came up with.