I have a custom package currently containing a table for client references and one table for their reference-categories in a many to many relationship (see my last thread).
My goal is now to dynamically create/update/delete individual resources for each reference under their respective category parent, which also should be created/updated/deleted dynamically from the data managed through a CMP.
I found a few similar threads and this old tutorial which goes in the right direction, but it’s not working fully (probably because the involved migxchildresources package is 11 years old) and I’m not sure how to adapt this to my needs of creating also the parent category resources for the references.
One additional tricky part might be to avoid duplicate resource creation and to create a weblink if the same reference is within two categories, so I don’t have duplicate pages (which I’m quite sure Google won’t like). But that’s already one step too far.
Any help in getting me in the right direction and to understand this better is highly appreciated. Also if there is a different, better method please let me know!
The main reason for me is that I want to display each references data on the frontend on their individual page. Also I want to have pages for the categories that list the assigned references and the user should then be able to click on each of them to display all the details.
I don’t understand what you mean by that, but if I can somehow generate my pages directly from the data in the existing tables, that’s fine with me, too.
You have categories and references in your custom database table and now you want to create a resource for every category and every reference.
Why not only create resources? What is the point of the data in the custom database tables?
What you usually want to avoid is duplicated data. The same information stored twice. Because this can quickly lead to data inconsistency.
Without knowing exactly what your goal is, I’d suggest either using resources for categories and references (and only a custom table for the mapping), or using custom database tables for everything and displaying the data on a single page. (You can also have different URLs for this single page with an extra like CustomRequest or a custom gateway plugin.)
Sorry to confuse you guys, I’m sure this is only because I don’t know the correct method to use here.
My main goal is to manage all the data only from within the CMP where the client has access to both the references and categories and this data being accessible on individual pages in the frontend.
In the front end my goal would be to have this page structure:
Portfolio
Category 1
Reference A
Reference B
Reference C
Category 2
Reference D
Reference A
Reference E
…
I hope this helps to clarify and to tell me what’s the best way to go here.
If you want all the data to be managed in custom tables, you could for example only create the “Portfolio” resource in MODX.
You can then create the category/reference navigation tree from the data in your custom tables.
Then let’s say the user clicks on “Reference A” (URL = portfolio/references/reference-a/ for example). You use CustomRequest or a custom gateway plugin to ‘redirect’ this request to the “Portfolio” page and display the information for the requested reference.
If the user clicks on a category (let’s say the URL is portfolio/categories/category-1/, you also load the “Portfolio” page, but this time display (for example) a list of references belonging to this category.
Basically you have 1 resource and the content of this resource is dynamic depending on the request.
You could of course also dynamically create a MODX resource for every item in your custom tables. But this is probably harder to implement and you would have to make code changes to at least the MIGX update processor. Also data inconsistency would be a problem, if users start to manipulate the created resources directly.
Okay, I think I want to go with the one resource and routing option.
While I piece the whole setup together, my first question would be:
I know I can generate an alias from a references name, e.g. using the filterPathSegment output modifier, but is there a way to find my reference/category again using this, or am I making my life a lot easier by simply creating proper alias fields for those?
So I added the alias field to my tables and I implemented the automatic alias generation from the linked thread. I was wondering if I could make this a bit more future proof for possible migx updates so I copied the update.php processor file into my own package in the exact same folder structure (processors -> mgr -> default -> update.php), but this doesn’t seem to work. Is there a way to make this more update friendly?
Also I’ve added index="index" to the fields in the schema (which was what I thought you meant by that, but the alias is actually generating duplicates with the same name. Did you mean something else?
Make sure that in your MIGX configuration, you set the “Package” field (under “MIGXdb-Settings”) to the name of the folder of your package (in core/components/) so that MIGX can locate your custom processor. (I believe it’s client in your case.)
When you add an index tag to the schema, there is a unique attribute that you can set → <index ... unique="true" ...>
Manually checking the database, I can see that despite me parsing, adding fields and updating indexes through migx, the index attribute unique is still set to false in the db. Is this a bug or did I do something wrong?
I believe the “Add Fields” and “Update indexes” functionality in MIGX is not 100% reliable. You may have to change the index manually (or delete the custom tables completely and recreate them again.)
While adjusting it manually I noticed that I first couldn’t change index unique to true as their were still duplicate entries so mysql throws an error. That’s probably why migx couldn’t do it either, but it would be nice if there was not a success message then.
Anyway, it’s working now, but if a duplicate entry would be generated I get an error popup saying quip.thread_err_save. Is this a lexicon reference? Why is it saying quip though?
Also moving on to the next steps of generating a working plugin, I tried to start with the basics, also following your video on the subject, but this doesn’t give me any results:
I’m still wondering if I did some of the class pathing wrong? I don’t fully understand yet, when to give the full path and when not to. I guess that’s also the issue here?
An error message gets added to the MODX error log → ... Unknown column 'clientKategorie.name' ...
Looking at the SQL query (that gets output as well) ... LEFT JOIN `modx_client_kategorien` `Kategorie` ON ...
the table modx_client_kategorien gets the alias Kategorie in this query (using the name of the relationship).
So your sortby line should instead be $c->sortby('`Kategorie`.`name`', 'ASC');
Probably for some weird historic reasons I’m not privy to.
As you have a custom “update”-processor now, just change the value in the code to a better fitting error message.
Alright, thanks to you guys I’m making good progress here. I ended up changing my mind over the basic page structure I want to have, so now my goal is this:
Portfolio* (/portfolio) - list all references
Reference (/portfolio/referencealias) - list individual reference
Services* (/services) - list all categories
Service (/services/categoryalias) - list all references of individual category
*only those sites have an actual resource
Turns out it’s quite simple to achieve this, using the customRequests extra and this snippet:
// add check if unkown url reference is given
if (isset($_GET['reference'])) {
$ref = filter_var($_GET['reference'], FILTER_SANITIZE_STRING);
$output = $modx->getChunk('portfolio.referenz', array(
'reference' => $ref
));
} else {
$output = $modx->getChunk('portfolio.referenzen');
}
return $output;
Like mentioned in the code, I need to add something to check if the given reference alias is actually there and if not probably redirect to a 404 page. Is there anything else I could/should improve?
One part I couldn’t figure out yet is how to call all references from a specific category alias? Here is my attempt, the tag [[+category]] is submitted through my other snippet, so it holds the category alias name, from which the references should be displayed.
I don’t know what the content of the chunk “portfolio.referenz” is (probably a call to migxLoopCollection), but here is what I would probably do:
<?php
$output = '';
if (isset($_GET['reference'])) {
$ref = filter_var($_GET['reference'], FILTER_SANITIZE_STRING);
// query the reference with the given alias from the database
$reference = $modx->getObject('Client\\Model\\clientReferenz', ['alias' => $ref]);
if ($reference) {
// a reference with this alias exists in the database table
$reference_arr = $reference->toArray(); // all reference fields as an array
$output = $modx->getChunk('portfolio.referenz', $reference_arr); // you can use the reference fields as placeholders in the chunk, e.g. [[+title]]
} else {
// a reference with this alias doesn't exist
$modx->sendErrorPage();
}
} else {
$output = $modx->getChunk('portfolio.referenzen');
}
return $output;
The class name is wrong. Isn’t it Client\Model\clientReferenz?
Thank you for the snippet example, I stumbled upon a few issues, though:
If I enter a non-existing url-alias, I get an endless loading page resulting in a blank page. I do have a dedicated 404 page setup and defined in the settings.
Additionally it seem the getObject call doesn’t like double backslashes (\\), I had to change them to singular ones to make it work (with existing aliases).
Also the snippet itself has to be called uncached to properly switch between contents if the url is changed (but maybe that was obvious before).
Good catch! But now I get all the references, I assume I still made a mistake in my &where property?
It looks like in the function sendErrorPage() the event “OnPageNotFound” is invoked again, which then probably runs the CustomRequest plugin again.
So I guess instead of $modx->sendErrorPage(); you have to execute something like this using sendForward:
&where=`{"`ReferenzKategorien`.`alias`": [[+category]]}`
“ReferenzKategorien” is the table client_referenzkategorie that doesn’t have an “alias” column. You either have to join all 3 tables, or run an additional query first, that determines the ID for a given category-alias.
In this case the two version should be exactly the same.
(This code returns → “the same”).
<?php
if ('Client\\Model\\clientReferenz' === 'Client\Model\clientReferenz') {
return "the same";
} else {
return "different";
}
In PHP, backslashes (\) in strings are always a bit confusing, as they are also used a an escape character. For example \n means “New Line”, \t is the “Tab” character etc. To avoid any problems (if for example the class name start with the letter n or t), I prefer using double backslashes where the first backslash “escapes” the second one to make it’s interpreted as a literal backslash.