Working with Express objects programmatically
Permalink 4 users found helpful
Hi,
Liking the new Express objects. However, documentation is lacking severely when it comes to working with Express in code. The couple of videos posted don't really cut the mustard if you want to do some serious work with these.
Actually, documentation has been a major problem with Concrete ever since 5.7 - I mean, just contrast the Developer docs for 5.4 and 5.7 and you'll see what I mean. Pretty useless.
What can be done? Shouldn't the C5 team either buckle down and create proper docs, or maybe get the community involved some more here? Huddled masses unite!
Anyway I digres, I have been working with Express objects a bit, and I will be documenting my efforts here for others to peruse. Maybe you'll find some of this useful.
Selecting an Express object by handle (user_bookmarks - think of this as "Marina" in the C5 guide), finding a specific Entry by custom attribute (here, user_id) and then list the one to many associations to another Express object called "Bookmark" (think of this as Boats in the C5 guide)
Here is another way of doing things. We just filtered our list of objects by an attribute (user_id) - but what if the object is already associated to something as an atribute? Here we have it associated to a User attribute, and in this way we can pull the list like so:
I will be posting more examples as I figure them out. Namely: Creating new Entries, associating an Object to a user attribute (will probably work the same way for pages) and maybe some more odds and ends.
Liking the new Express objects. However, documentation is lacking severely when it comes to working with Express in code. The couple of videos posted don't really cut the mustard if you want to do some serious work with these.
Actually, documentation has been a major problem with Concrete ever since 5.7 - I mean, just contrast the Developer docs for 5.4 and 5.7 and you'll see what I mean. Pretty useless.
What can be done? Shouldn't the C5 team either buckle down and create proper docs, or maybe get the community involved some more here? Huddled masses unite!
Anyway I digres, I have been working with Express objects a bit, and I will be documenting my efforts here for others to peruse. Maybe you'll find some of this useful.
Selecting an Express object by handle (user_bookmarks - think of this as "Marina" in the C5 guide), finding a specific Entry by custom attribute (here, user_id) and then list the one to many associations to another Express object called "Bookmark" (think of this as Boats in the C5 guide)
use Concrete\Core\Support\Facade\Facade; use Concrete\Core\Support\Facade\Express; use Concrete\Core\Entity\Express\Entity; use Concrete\Core\Express\Entry\Search\Result\Result; use Concrete\Core\Express\EntryList; class Bookmarks { public $entityManager; public $app; public function __construct() { $this->app = Facade::getFacadeApplication(); $this->entityManager = $this->app->make('database/orm')->entityManager(); } /* Get user bookmarks from Express object with corresponding User ID attr set */ public function getUserBookmarks($userid) {
Viewing 15 lines of 32 lines. View entire code block.
Here is another way of doing things. We just filtered our list of objects by an attribute (user_id) - but what if the object is already associated to something as an atribute? Here we have it associated to a User attribute, and in this way we can pull the list like so:
$ui = UserInfo::getById($userid); $ui_bookmarks = $ui->getAttribute("user_bookmarks"); if (is_object($ui_bookmarks)) { //Good, the user has a bookmark attr value associated, grab the entity so we can read from it $entries = $ui_bookmarks->getSelectedEntries()->getValues()[0]->getBookmark(); if (count($entries)) { foreach ($entries as $bookmark) { //This nastyness gets a specific Select Attribute Option Value $type = $bookmark->getBookmarkType()->getSelectedOptions()->getValues()[0]->getSelectAttributeOptionValue(); $data[] = Array( "type" => $type, "entity_id" => $bookmark->getBookmarkEntityId(), "note" => $bookmark->getBookmarkNote() ); }
Viewing 15 lines of 18 lines. View entire code block.
I will be posting more examples as I figure them out. Namely: Creating new Entries, associating an Object to a user attribute (will probably work the same way for pages) and maybe some more odds and ends.
These are helpful. You should consider posting them to the documentation site as either specific developer tutorials or additions to the Express documentation itself.
Hey Andrew
Alrighty, I might do that. I'll start with maintaining this post for the next few days and document my efforts and make everything pretty. I might then hit you up for instructions on submission to your docs - if my approach cuts the mustard, that is. I mean, I found a way to do these things - but it's not guaranteed that it's the best, nor the easiest, way of going about it.
Keep up the good work! :O)
Alrighty, I might do that. I'll start with maintaining this post for the next few days and document my efforts and make everything pretty. I might then hit you up for instructions on submission to your docs - if my approach cuts the mustard, that is. I mean, I found a way to do these things - but it's not guaranteed that it's the best, nor the easiest, way of going about it.
Keep up the good work! :O)
Andrew
It would be great if you could give me a hint here as I am stuck on the association step.
- This code runs without errors, but I am not getting any association made between the entries.
- Do I need to read out existing entries and pass those along with the new one to Applier::associateOneToMany ?
It would be great if you could give me a hint here as I am stuck on the association step.
- This code runs without errors, but I am not getting any association made between the entries.
- Do I need to read out existing entries and pass those along with the new one to Applier::associateOneToMany ?
$bookmark = new Entry(); $bookmark->setEntity($bookmark_entity[0]); //Some DB voodoo here $this->entityManager->persist($bookmark); $this->entityManager->flush(); //Yay! Let's set some data $bookmark->setAttribute("bookmark_type",$data["bookmark_type"]); $bookmark->setAttribute("bookmark_entity_id",$data["bookmark_id"]); $bookmark->setAttribute("bookmark_note",$data["note"]); //Now we associate it with the correct User Bookmarks entry $association = new OneToManyAssociation(); $association->setSourceEntity($user_bookmarks_entity[0]); $association->setTargetEntity($bookmark_entity[0]); $this->entityManager->persist($association); $this->entityManager->flush();
Viewing 15 lines of 18 lines. View entire code block.
We will be releasing concrete5 8.1.0 early next week. One of its improvements is an improved Express object builder API. I would recommend checking out these docs and upgrade to 8.1 when it is available:
https://documentation.concrete5.org/developers/express/programmatica...
https://documentation.concrete5.org/developers/express/programmatica...
Do singular handles and plural handles need to be distinct?
Supposing I was creating a sheep object. I can have one sheep or many sheep. The singular and the plural are the same.
Supposing I was creating a sheep object. I can have one sheep or many sheep. The singular and the plural are the same.
It would be extremely helpful if someone (Andrew?) could work up a set of simple code examples for CRUD operations against express.
- C: Insert an entry
- R: Retrieve an entry and get attribute values
- U: Change attribute values and save
- D: Delete an entry.
It is slow going slogging through the core code and limited documentation for clues.
- C: Insert an entry
- R: Retrieve an entry and get attribute values
- U: Change attribute values and save
- D: Delete an entry.
It is slow going slogging through the core code and limited documentation for clues.
I have added this documentation. NOTE: it will require concrete5 version 8.1.0, which will be arriving today.
https://documentation.concrete5.org/developers/express/creating-read...
https://documentation.concrete5.org/developers/express/creating-read...
Excellent! Thank you Andrew.
Excellent work Andrew!
I would like to add a few notes, which you might want to add to the docs:
- You need to (this might depend on context) include a use Concrete\Core\Support\Facade\Facade; clause in addition to use Express;
- When retrieving an Express entry from a User attribute (this is probably the case for pages as well) you get an object of type ExpressValue. This means you have to do something like this to get the Entry object (in order to do associations):
- When calling ->associateEntries()->set* on an Entry, you have to include all the entries you want associated. That is, this is a destructive update so you need to read out existing associations and pass those through. I dug around but could not find an addEntry() or equivalent ...
Please let me know if there is an easier way to do some of this... Here is my full code I ended up with for reading out and adding entries from a User attribute, with corresponding associations (much cleaner than my first attempts ... Thanks again Andrew!)
I would like to add a few notes, which you might want to add to the docs:
- You need to (this might depend on context) include a use Concrete\Core\Support\Facade\Facade; clause in addition to use Express;
- When retrieving an Express entry from a User attribute (this is probably the case for pages as well) you get an object of type ExpressValue. This means you have to do something like this to get the Entry object (in order to do associations):
$ui_bookmarks = $ui->getAttribute("user_bookmarks"); $entry = $ui_bookmarks->getSelectedEntries()->getValues()[0];
- When calling ->associateEntries()->set* on an Entry, you have to include all the entries you want associated. That is, this is a destructive update so you need to read out existing associations and pass those through. I dug around but could not find an addEntry() or equivalent ...
Please let me know if there is an easier way to do some of this... Here is my full code I ended up with for reading out and adding entries from a User attribute, with corresponding associations (much cleaner than my first attempts ... Thanks again Andrew!)
use Concrete\Core\Support\Facade\Facade; use Express; use UserInfo; class Bookmarks { public $entityManager; public $app; public function __construct() { $this->app = Facade::getFacadeApplication(); $this->entityManager = $this->app->make('database/orm')->entityManager(); ini_set('xdebug.var_display_max_depth', 7); ini_set('xdebug.var_display_max_children', 256); ini_set('xdebug.var_display_max_data', 1024); } /* Get user bookmarks from Express object with corresponding User ID attr set */
Viewing 15 lines of 61 lines. View entire code block.
Thank you very much, Andrew! It works like a charm.
One question: How can I make many to many associations? I always get
when I do
One question: How can I make many to many associations? I always get
Argument 1 passed to Concrete\Core\Express\Association\Applier::associate() must be an instance of Concrete\Core\Entity\Express\Association, null given
when I do
$entry->associateEntries()->setRules([$rule1, $rule2]);
Hey
I have been battling this as well. It turns out that you have to make sure that you are using the correct plural form of the object you are trying to associate with. So check if your object handles are "rule" and "rules" respectively
Also, I _think_ it's required that you set up a form for one (or both?) objects where you add the association.
Anyway, that seemed to do the trick for me when I ran into that one.
@Andrew: Is there a non-magic method for the line kfidel has problems with here? It would make it easier to debug methinks if you could do something like
Or some equivalent.
I have been battling this as well. It turns out that you have to make sure that you are using the correct plural form of the object you are trying to associate with. So check if your object handles are "rule" and "rules" respectively
Also, I _think_ it's required that you set up a form for one (or both?) objects where you add the association.
Anyway, that seemed to do the trick for me when I ran into that one.
@Andrew: Is there a non-magic method for the line kfidel has problems with here? It would make it easier to debug methinks if you could do something like
$entry->associateEntries()->setAssociationByHandle("rule",[$rule1, $rule2]);
Or some equivalent.
Hmm, I tried many things, singular and plural. Nothing worked for me. I also tried to pass an object instead of an array, a.s.o.
Any hints about that problem?
I'm seeing the same thing here. Creating the objects programmatically works as the docs suggest, but when attempting to associate, it's a no go. I don't get any errors thrown, but when I attempt to get the association after setting it, its null.
this is because the documentation is wrong and was rushed...
the correct will be, using the example of documentation:
the correct will be, using the example of documentation:
$student1 = Express::buildEntry('student') ->setStudentFirstName('Andrew') ->setStudentLastName('Embler') ->save(); $student2 = Express::buildEntry('student') ->setStudentFirstName('Jane') ->setStudentLastName('Doe') ->save(); $teacher = Express::buildEntry('teacher') ->setTeacherFirstName('Albert') ->setTeacherLastName('Einstein'); $teacher = $teacher->save(); $teacher->associateEntries()->setStudents([$student1, $student2]);
I think I reached the same problem.
Is there a non-magic-function to reach the association?
returns
Is there a non-magic-function to reach the association?
$tukey_object = Express::buildEntry('tukeys')->setKey($tukey)->save(); $links = []; foreach ($institute as $institut) { $tmp++; $link = Express::buildEntry('link') ->setInstitut($institut) ->setLink('https://www.default.de') ->save(); array_push($links, $link); } $tukey_object->associateEntries()->setLinks($links);
returns
Argument 1 passed to Concrete\Core\Express\Association\Applier::associate() must be an instance of Concrete\Core\Entity\Express\Association, null given, called in /homepages/20/d310593786/htdocs/relaunch2019/concrete/src/Express/EntryBuilder/AssociationUpdater.php on line 55
I know this is an old thread but for reference the way to do this is:
I had to actually go read the code to find this information though. It's not documented anywhere :(
$entry->associateEntries()->associate($handle, $association);
I had to actually go read the code to find this information though. It's not documented anywhere :(