I have a Sitecore 6.6 solution running MVC and Razor.
I am have the following line;
#Html.Raw(Sitecore.Context.Database.GetItem(myList.Children.LastOrDefault()["myField1"]).Fields["myField2"])
The above works fine, except the order it sorts in. I need to get the latest item based on the __Created field. LastOrDefault seems to take the last item in the tree, which does not always correspond to the actual latest created item.
Can it be done in one line as the above?
How about this:
myList.GetChildren()
.OrderBy(x => x[Sitecore.FieldIDs.Created])
.Reverse()
.LastOrDefault()
I guess this would work too:
myList.GetChildren()
.OrderBy(x => x.Statistics.Created)
.Reverse()
.LastOrDefault()
Keep in mind though that first all the children will be retrieved and then the sorting will happen.
If you have a lot of children it will be slow.
One general note, you shouldn't do this in the View really. You're putting too much logic in there. Your model should provide the item that you need (last created).
Related
This is the pattern I find myself running into:
I start making an app, and I use findAll() to get a list of [something random].
Once the app is being tested with serious data, the number of random resource instances will grow. I need to limit the number of resource instances on screen. I need to start paginating them. For this I need query string support. E.g. page[offset].
So findAll(criteria) is replaced by query(criteria, querystring).
This is a pattern so much that findAll() is starting to look like a development placeholder for query() to be used later.
I'm probably misunderstanding the use for findAll(). Is it true findAll() cannot use pagination at all (without customizing adapter code)? Can someone explain in what cases findAll() should be used?
I personally use the findAll method for fetching data that appears in various drop-downs and short lists that cannot be filtered by the user. I use query and queryRecord for pretty much everything else.
Here are a couple of particularities of findAll that can be misleading:
findAll returns all of the records present in the store along with the data that is fetched using the record's adapter.
The return of findAll is two-fold, firstly you will receive the content of the store and then it will be refreshed with the data fetched using the adapter, this behavior can be overridden using the reload flag.
To expand on Jean's answer, findAll does just that, finds all! If you had entities such as "post types" where you have [advertisement, blog, poem], findall makes sense, because you are pulling these 3 things all the time (for example in a "post creator").
Query is more precise. Say You had an api returning every car you have ever seen.
Say you had a "car" model with properties "color" and "bodyStyle"
You could use:
// find all red cars -> /cars?color=red
store.query('car', {color: 'red'});
// find all cars that are coupes -> /cars?bodyStyle=coupe
store.query('car', {bodyStyle: 'coupe'});
To your question on pagination, this is typically implemented on the API. A popular pattern is to accept/return "page" and "count" properties. These are typically found in an API payload's "meta" property.
So if you wanted to look through all cars you know of/have in your database:
// find first 10 cars -> /cars?count=10&page=1
store.query('car', {count: 10, page: 1});
// on the next page, find the next 10 cars -> /cars?count=10&page=2
store.query('car', {count: 10, page: 2});
It is worth nothing that to further your own research you should look into query parameter binding on controllers to ease the labor needed to implement a solution like this.
https://guides.emberjs.com/release/routing/query-params/
In the examples in that link you can see how you can transition to routes and use the query parameters in your store requests to fetch relevant data.
In short, findAll() is great for finding a finite set of easy to represent information, typically types of entities.
query() is great for any filtered set of results based on a criteria, as you mentioned.
Happy Coding :)
If you want "all" record of a type I would recommend using query + peekAll, this is more or less what findAll does under the hood but without various timing issues / freshness issues that findAll is subject to.
query is generally a much better API because it lets you paginate, and most apps with data of any consequence eventually hit a point they are forced to paginate either for rendering concerns or data size concerns.
In my current model I have 2 entities: Student and StudentOrdersHistory. We're using a history table to record all the orders from the student.
However, we particularly need to work with the latest order history. At first, I used some circular reference like the following:
StudentOrderHistory.student_id --> Student
Student.latest_order --> StudentOrderHistory
The reason for this is that we expect the relation to have hundreds of rows (student here is just an example to make it simple to ask what I need), but since we mostly need to work with the last one, we figured it's pointless loading all of them and then doing some $student->orders->last(), since it'd have to load all the records.
Needless to say, this implementation brought a pain when deleting a Student, as the circular reference won't let me do it without first having to delete the Student.latest_order reference.
Is there anyway I can load into a Student property (like Student::$latestOrder) only the latests one using DQL?
Sure, but you might not be able to do it directly through the Student object. Sacrificing this convenience should get your the performance improvement you want, and in fact doctrine best practices suggest constraining relationships as much as possible.
I haven't tested this code, but you probably know how to get it working. I'd add a method to the OrderRepository, like
public function getLatestForStudent(Student $student)
{
$this->findOneBy(['student' => $student], ['created']);
}
I have a form here with a nested table - where each table can dynamically grow, i.e., the inner table (w/ Transit No and Account No) and the outer table (Accounts by ID No). Here is an example:
(Behind the buttons:
Add - $.parent.tbl.Row.instanceManager.addInstance();
Remove - $.parent.instanceManager.removeInstance(this.parent.index); (In
production I make sure there is at least one row to remove...)
In the definition for each table I do not have checked 'Repeat Table for Each Data Item'. This works great. However I did try with that checked and the outcome was the same.
Now, when I email the form and open the attachment, this is what I see:
You can see that the second table didn't make it, and apparently a row was added to the inner table in the first, without any data.
Any ideas on what's going wrong here? And what I can do about it?
Unfortunately I'm not sure what's wrong with your form but I have made a similar form that works - so I can show you how I did it and list a few things that I can think of that can cause problems.
This is what my form looks like and when I e-mail it, it comes out exactly the way it is:
(It has repeatable parent- and childsubforms like yours)
I did it entirely with JS though, no FormCalc and Dollar $igns :D
When a button is pressed I call a function from a Scriptobject.
These are the main parts of my script inside my functions:
Adding a Subform:
var oNewInstance = subform.instanceManager.addInstance(1);
Deleting a Subform:
if (subform.instanceManager.count > subform.instanceManager.occur.min)
{
subform.instanceManager.removeInstance(subform.index);
}
And these are my subforms' properties (in German, but you can figure it out :P):
Your problem might also have completely other reasons though, make sure you don't have any changes in an initialize,docReady, preSubmit and similar actions that occur between sending and opening the sent PDF.
Also before sending it as an e-mail you have to save it in Acrobat as a Reader Extended PDF:
Besides that I've noticed that sometimes problems can occur due to the target version (Selectable in LCD under File > Form Properties > Defaults).
It helped me sometimes to set it to the newest one.
I am almost done with my first production-ready django-project. I got one big problem left:
I got an article-search-view that renders a list of found articles. Pagination is working just fine for the resultlist. When I click on the article-title the object-detail-page opens. What I want: previous- and next-result-links on the object-detail-page.
I tried several approaches to similar problems but didn't find a working solution. If I try to use a paginator with only one article (for the object-detail-page) I need to know that paginator-index in the resultlist. But how?
Even the .get_next(previous)_by_foo-Method is not really usable in this scenario AFAICT. Or am I missing something obvious here? Thanks for any help in advance!
Paginator from django works with lists. A way of searching indexes in lists it's like that:
['aaa', 'bbb', 'ccc'].index('bbb') # result: 1
or so like this:
model = object()
[object(), object(), model].index(model) # result: 2
Hope that gives you a hint on how you find paginator-index on your list.
If you want to get a link, at object-details-page, to next item from search-result, you must get next item from the search-result. To get a next item you need to perform the same search query which was executed in search-page and apply some extra filters to get only the next item from that list. But here you have a problem: you only have object-id in object-details page, you don't have the search-term. Without search-term you won't be able to create the search-query. That means you need to get search-term somehow. How do you get that search-term? You need to pass it from the search-result-page somehow. You can save the search-term in session/cookie, or, maybe better: you can pass it via a GET parameter to object-details page. Now when you search-term in object-display page, you can perform a search-query, and from that query you can select the next and the previous objects.
I think now you should be able to implement that. If not you could show some of your code of object-details view, maybe someone will write some code for you.
you could use this
next »
« previous
I have the following for-each select;
"sc:item($myFolder,.)/descendant::item [#template='myTemplate']". According to Sitecore's own profiler this returns crawls through 16.000 items, although there are only approx 1700 items with the mentioned template.
Could this be optimized - if so, how?
The for-each is not the problem, but the XPath query used is.
The descendant axis is usually expensive because it requires the engine to do a complete deep search, without the possibility of eliminating paths. So you may want to replace this with a pattern which restricts the required search path.
Maybe using keys would help, but without deeper knowledge of the XML to be processed and the operations performed this is hard to tell.
If you can convert your xsl:for-each to a xsl:template, it should help with performance, as most XSLT processors are optimized for template matching.
"Do you have an example of this? It's a basic for-each that select a list of items based on the template, and then sort by a number field and shows the 3 top (so its basicly a top 3 list)"
Based on what you're trying to accomplish, I think you would be much better off putting this logic into a save handler and organizing your items when a content author saves an item based on the template you're looking for.
For example (and this is a very rough example), create a "Top 3" folder in your content tree. Then, write a save handler that checks the TemplateID of an item as it's being saved. If the saved item's TemplateID matches the TemplateID you're looking for, select all the items in your tree based on the TemplateID and process them with whatever sorting logic you need. As you find an item that belongs in the "Top 3" folder, move the item to the "Top 3" folder.
Then, in your rendering code, you simply have to get the "Top 3" folder and retrieve it's children. This will be MUCH faster than attempting to look through 1700 items every time the rendering is loaded.
If I need to do any particularly difficult searches in an XSLT, then I often define an XSLT helper method to perform the search, as the .Net API has much better facilities available.
In your case, I'd consider creating a .Net function to do the job, either using the Link Manager (as Mark Cassidy suggests) or using Sitecore fast queries to retrieve the items (fast queries are parsed and run as SQL queries).
To register a class as an XML Extension Method library, open web.config, locate the /configuration/sitecore/xslExtensions node and add an entry with your class' full type name and an XML namespace (the URL doesn't actually have to exist).
To use your function, register it in the namespaces at the top of your XSLT (and remember to add the namespace to the exclude-result-prefixes stylesheet attribute). You should then be able to call `namespace:YourFunction().
For more detailed information on XSLT extension methods, see the presentation component reference, page 40.
The reason the XSLT crawls through 16,000 items when you only have 1700 matches is that the 16,000 represents the number of nodes tested, not the number of nodes returned. Sitecore XSLT performance is dependent on your actual data structure, if you have a flat structure with lots of nodes (e.g. everything in one folder) all xslt processing will suffer. You might consider optimising your data structure into something deeper and more branched to aid XSLT performance. I believe Sitecore themselves recommend that you have no more than 100 items in any location (i.e. never more than 99 siblings for any item).
It can be optimised, but the easiest approach would be to reorganize your content architecture to better suit your needs and requirements.
Straight from XSLT there is no easy solution, but with a bit of coding you can fAirly easily get all your content items based on your specific template. It would look something like this:
Item myTemplate = Sitecore.Context.Database.GetItem( "id-of-your-template" );
LinkItem[] li = Sitecore.Globals.LinkDataBase.GetReferrers( myTemplate );
// li now holds a list of LinkItem for items that are based on your template
List<Item> myItems = new List<Item>();
foreach( LinkItem l in li )
myItems.Add( li.SourceItem() );
// your additional processing/filtering here
Keep in mind, this is a very non-specific example of how this can be achieved. I would need to know a lot more about your solution to come up with a better response. And I still feel, the best approach is probably still to look at your information architecture.