Pass the current Model down to a more specific shape - orchardcms

TL;DR: How do I pass the model down into the next shape?
I have modified the Orchard.Search modules search results view in my theme (Views\Orchard.Search\Search\Index.cshtml), and split it out into several scenarios. I test for one result and just redirect straight through and test for no search terms and multiple search search results.
I would like to put these scenarios in separate .cshtml views but these specialised shapes need access to the main search result model (#model Orchard.Search.ViewModels.SearchViewModel).
I tried using Arguments.From to make a dynamic object which works but then I have to cast everything in the view back to its correct data type because the next shape down thinks every property on its model is of type dynamic.
Instead I've tried to figure out how to just pass the whole Model to the next shape:
#if (HasMultipleResults())
{
var shapeFactory = (IShapeFactory)New;
// this doesn't work
var multipleResultsShape = (dynamic)shapeFactory.Create("Search_MultipleResults", Model);
#Display(multipleResultsShape);
return;
}
I haven't found a way to get it working yet, can anyone educate me please?

If the first argument of the dynamic method is a type, then this will be used as the model.
New.YourShape(typeof(YourType))
See this video for the details: https://www.youtube.com/watch?v=gKLjtCIs4GU&feature=youtu.be&t=1127

Related

Yii2 Adding SearchModel Many-to-Many relationships produces duplicative results

I have a model that has six many-to-many relationships. When I add the joinWith entries in the SearchModel and, then, look at what gets returned in the index.php view (with no search parameters applied), it shows many more rows than are actually in the base model. Upon further inspection, I find that multiple duplicative rows are being returned. It also throws pagination way off. For instance, if I add just one joinWith in the SearchModel and I've got pagination set to 10 rows per page, here's what happens. In the base model which has 175 rows, the first page will show "1-4 of 425 items". The second page will show "111-16 of 425 items." and the last item of the first page is duplicated at the top of the second page. To give some background:
The base model has the following relationships:
/**
* #return \yii\db\ActiveQuery
*/
public function getHerbalHerbsHerbalPreparations()
{
return $this->hasMany(\common\models\HerbalHerbsHerbalPreparations::className(),
['herbal_preparation_id' => 'id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getHerbalHerbs()
{
return $this->hasMany(\common\models\HerbalHerbs::className(), ['id' => 'herbal_herb_id'])
->viaTable('herbal_herbs_herbal_preparations', ['herbal_preparation_id' => 'id']);
} */
In the SearchModel, I've got this entry:
$query = HerbalPreparations::find()->joinWith('herbalHerbs');
Please note that it makes no difference if I add the optional joinWith parameters for eager loading and join type. `$query = HerbalPreparations::find()->joinWith('herbalHerbs', true, 'LEFT JOIN'); produces the same results.
Without any joinWith, the index.php view looks like this:
With the joinWith described above, page 1 of index.php looks like this:
And page 2 of the index.php view looks like:
Note the duplication of the last row on page 1 and the first row of page 2.
Needless to say, this problem becomes exponentially worse if I specify the additional many-to-many relations with joinWith.
If I actually enter a search parameter using the GridView filter or a search form, it works perfectly. The problem is that the index.php view needs to work properly with no search parameter.
This seems like it would be a common problem for anyone using Yii2 in any kind of advanced application. I'm sure I must be doing something wrong, but I searched Google until I'm exhausted and haven't been able to find anything helps.
`
I can't take responsibility for this answer. Actually, Newbie on the Yii forum gave this answer. It's actually rather simple — just add a groupBy('id'). For instance
$query = HerbalPreparations::find()->joinWith('herbalHerbs', true, 'LEFT JOIN')->groupBy(['id']);
I suppose I should have been able to figure this one out from a basic knowledge of SQL, but I've never had to use groupBy in this way.
This seems like such a common need that I would think it would be documented and found just about everyplace on a simple search. Unfortunately, it doesn't appear to be.

Xpages: how to have a custom function return type ahead values

I have a SSJS object/function that does a custom full text search, as the results need to be in a specific order defined by my client. This function works fine in the search results view. It has a method to build the search query (add the * and the AND and all that), a property that returns the sorted results and I just added a method that returns the HTML needed for the type ahead.
My object is called SortedSearchResults. It needs to be instantiated with the search query in order to get the results and the type ahead HTML. How would I code that in the the type ahead values, so I don'T end up creating one SortedSearchResults object each time a letter is added to the type ahead field?
Would I be better off with a session managed bean? Would that make it easier? Would it make it faster? The search is limited to a maxiumum of 15 results.
Sor far, I only used SSJS code, but I am not sure how what I should do to avoid a memory hole. Here is the current code:
//TODO Memory management???
var results = new SortedSearchResults( getComponent("inputSearch").getValue());
return results.typeAheadValues;
How can I optimize this code so I don'T create unecessary "var results"? Or is it OK that way???
Thanks :)

In Orchard CMS, how can I get all items of a particular content type based in the values of an attached part's properties

I'm using Orchard 1.7.2.
I have created a new content type called PropertyImage of stereotype Media. I also created a part called PropertyPart and attached that part to my PropertyImage content type. This allows a user to pick a product when uploading a PropertyImage (ie to say 'This image is of this property').
So far so good.
Now what I'd like to do is query for all PropertyImages that have a PropertyPart attached to them where the associated property is x, y, or z.
This is what I have so far:
var images = _orchardServices.ContentManager
.Query<PropertyPart, PropertyRecord>()
.Where(p => p.PropertyId == id)
.ForType(new[] { "PropertyImage" });
This however will only return a collection of PropertyParts, which is not what I want, because I want the whole PropertyImage Content Item. How can I do this?
I should point out that properties come from an external source, and are therefore not content items.
Edit
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!
As soon as I asked this question, I realised I could just append my query with this:
.List().Select(p=>p.ContentItem)
Sometimes it just helps to talk your problem through!

Magento: Attribute always returns default value in catalog view, works fine in product view

I've created a new Yes/No attribute for products. I've extended the Product model to do some custom logic and the custom functions are working everywhere.
When I initially tried getting the custom attribute value, I ran into some issue. Magento wasn't loading it for me, so calls to $product->getMyAttributeName() did nothing. In the product views, I got it working with this additional function:
public function getAttrVal($attr_name)
{
return $this->getResource()->getAttribute($attr_name)->getFrontend()->getValue($this);
}
So that worked great when viewing a product on the frontend. It would fetch the assigned value if set, or the default if not.
When I view any Category (grid of all products in that category), the same exact code is being executed. But my getAttrVal() function always returns the default value, even if I've explicitly set this value for my product.
I can't, for the life of me, figure out why the attribute loads correctly in the Product view but the Category view always grabs the default value, despite running the same exact code. Any thoughts?
Because Magento uses an EAV model for its extensible catalog attributes, not every attribute may be carried over onto every page. Try fiddling with the "displayed in catalog" and other related variables on the attribute, and it may fix your problem. If not, say so and we can try other things.
Hope that helps!
Thanks,
Joe

Help to restructure my Doc/View more correctly

Edited by OP.
My program is in need of a lot of cleanup and restructuring.
In another post I asked about leaving the MFC DocView framework and going to the WinProc & Message Loop way (what is that called for short?). Well at present I am thinking that I should clean up what I have in Doc View and perhaps later convert to non-MFC it that even makes sense. My Document class currently has almost nothing useful in it.
I think a place to start is the InitInstance() function (posted below).
In this part:
POSITION pos=pDocTemplate->GetFirstDocPosition();
CLCWDoc *pDoc=(CLCWDoc *)pDocTemplate->GetNextDoc(pos);
ASSERT_VALID(pDoc);
POSITION vpos=pDoc->GetFirstViewPosition();
CChildView *pCV=(CChildView *)pDoc->GetNextView(vpos);
This seem strange to me. I only have one doc and one view. I feel like I am going about it backwards with GetNextDoc() and GetNextView(). To try to use a silly analogy; it's like I have a book in my hand but I have to look up in it's index to find out what page the Title of the book is on. I'm tired of feeling embarrassed about my code. I either need correction or reassurance, or both. :)
Also, all the miscellaneous items are in no particular order. I would like to rearrange them into an order that may be more standard, structured or straightforward.
ALL suggestions welcome!
BOOL CLCWApp::InitInstance()
{
InitCommonControls();
if(!AfxOleInit())
return FALSE;
// Initialize the Toolbar dll. (Toolbar code by Nikolay Denisov.)
InitGuiLibDLL(); // NOTE: insert GuiLib.dll into the resource chain
SetRegistryKey(_T("Real Name Removed"));
// Register document templates
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CLCWDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CChildView));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCmdLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
// The window frame appears on the screen in here.
if (!ProcessShellCommand(cmdInfo))
{
AfxMessageBox("Failure processing Command Line");
return FALSE;
}
POSITION pos=pDocTemplate->GetFirstDocPosition();
CLCWDoc *pDoc=(CLCWDoc *)pDocTemplate->GetNextDoc(pos);
ASSERT_VALID(pDoc);
POSITION vpos=pDoc->GetFirstViewPosition();
CChildView *pCV=(CChildView *)pDoc->GetNextView(vpos);
if(!cmdInfo.m_Fn1.IsEmpty() && !cmdInfo.m_Fn2.IsEmpty())
{
pCV->OpenF1(cmdInfo.m_Fn1);
pCV->OpenF2(cmdInfo.m_Fn2);
pCV->DoCompare(); // Sends a paint message when complete
}
// enable file manager drag/drop and DDE Execute open
m_pMainWnd->DragAcceptFiles(TRUE);
m_pMainWnd->ShowWindow(SW_SHOWNORMAL);
m_pMainWnd->UpdateWindow(); // paints the window background
pCV->bDoSize=true; //Prevent a dozen useless size calculations
return TRUE;
}
Thanks
Hard to give you good recommendations without knowing what your program shall do. I have only a few general remarks:
Your InitInstance does not look very messed up for me. It's pretty much standard with a bit of custom code in it.
Also the ugly construction to retrieve the first view from the application class (the chain GetDocTemplate -> GetDoc -> GetView) is standard to my knowledge. I actually don't know another way. You might think about moving it into a separate method like CChildView* CLCWApp::GetFirstView() but well, that's only cosmetic as long as you need it only at one place.
What you are doing and which data you are placing in your Document class and in your View class(es) is more a semantic question if you only have one view. (You have only one document anyway because it's an SDI application.). From a technical viewpoint often both is possible.
But to be open for (perhaps) later extensions to more than one view and to follow the standard pattern of a doc/view architecture there are a few rules of thumb:
Data which exist and have a meaning independent of the way to present and view them (a document file, a database handle, etc.) belong to the document class. I don't know what your pCV->OpenF1(cmdInfo.m_Fn1) ... and so on does but if it's something like a file or filename or a parameter to be used to access data in any way OpenF1 might be better a method of the document class.
Methods which do any kind of data processing or modification of your underlying data belong to the document class as well
Data and methods which are only needed for a specific way to display a document belong to a view class (for instance a selected font, colours, etc.)
On the other side: If you have a fixed number of views which open with the document it might not be wrong to put view specific data into the document, especially if you want to make those view parameters persistent. An example would be a file with some statistical data - your document - and a splitter frame with two views: one displays the data as a grid table and the other as a pie chart. The table has "view data" describing the order of and width of columns, the pie chart has data to configure the colours of the pie pieces and the legend location, for instance. If you want to make sure that the user gets the last view configuration displayed when he opens the document file you have to store these view parameters somewhere. It wouldn't be wrong or bad design in my opinion to store those parameters in the document too, to store and retrieve them from any permanent storage, even if you need them only in the view classes.
If your application allows to open an unlimited number of views for a document dynamically and those views are only temporary as long as the application runs, storing all view configuration parameters directly in the view classes seems more natural to me. Otherwise in the document you would need to manage any kind of dynamic data structure and establish a relationship between a View and an entry in this data structure (an index in an array, or a key in a map, etc.)
If you are in doubt whether to place any data in the document or view class I'd prefer the document because you always have the easy GetDocument() accessor in the View class to retrieve members or call methods of the Doc. To fetch data from the View into the Document requires to iterate through the list of views. (Remember: Doc-View is a 1-n relationship, even in a SDI application.)
Just a few cents.

Resources