Express - Add Attribute to existing Express Object Programmatically

Hey so I see that in the documentation I can programmatically add Attributes to Express Objects when they get created, however I need a way to programmatically add Attributes to existing Express Objects. As in… without removing/re-creating the Express Objects.

How do I go about doing that?

@BloodyIron Unfortunately, there’s no easy way to do that, but you can follow how core ImportExpressEntitiesRoutines class does.

Man I’m really not seeing how exactly to adjust that to what I’m trying to do :confused:

Also, I don’t know how $entityNode should be defined in the first place.

When you visit Concrete github account, there is a “migration_tool” package there.
Install package appropriate to your Concrete version and play with it a little.
It allows you both import and export Concrete stuff (pages, attributes, files and many more).

The point is, that “Migration tool” will create xml file that holds information about your Express object (which can be used instead writing a lot of php code).

Export your Express object and download generated xml file.
You can trim stuff that you don’t need.
For example this one will insert text attribute to entity.

<?xml version="1.0" encoding="UTF-8"?>
<concrete5-cif version="1.0">
    <expressentities>
        <entity id="c7f4f05f-1a67-11ee-bf38-0242ac1e0002" handle="teacher" >
            <attributekeys>
                <attributekey handle="test_name" name="Test name" package="" searchable="1" indexed="1" type="text"
                              category="">
                    <type placeholder=""/>
                </attributekey>
            </attributekeys>
        </entity>
    </expressentities>
</concrete5-cif>

Copy that file somewhere on your server.

Then you can “reuse” code that hissy mentioned.
For example:

$sx = simplexml_load_file('application/bootstrap/export.xml'); // change path!
$expressObjectHandle = 'teacher'; // change handle!

$em = \Database::connection()->getEntityManager();

if (isset($sx->expressentities)) {
    foreach ($sx->expressentities->entity as $entityNode) {
        $entity = $em->getRepository('Concrete\Core\Entity\Express\Entity')->findOneBy(['handle' => $expressObjectHandle]);

        // Import the attributes
        if (isset($entityNode->attributekeys)) {
            $app = \Concrete\Core\Support\Facade\Application::getFacadeApplication();
            $category = new \Concrete\Core\Attribute\Category\ExpressCategory($entity, $app, $em);
            foreach($entityNode->attributekeys->attributekey as $keyNode) {
                $attributeKey = $category->getAttributeKeyByHandle((string) $keyNode['handle']);
                if (!$attributeKey) {
                    $type = $app->make('Concrete\Core\Attribute\TypeFactory')->getByHandle(
                        (string)$keyNode['type']
                    );
                    $category->import($type, $keyNode);
                }
            }
        }
    }
}

$em->flush();

This is very rough code, but should give you idea where to start.
Backup your database in case something gone wrong.

Export/Import really isn’t what I’m seeking here, as that generally implies an outage, and I don’t see how that really is what I’m talking about, which is adding Attributes to the existing Express Object without re-creating it. I’m roughly trying to build out a DevOps workflow here, to say in short-form, and this is one of the areas I haven’t found a workable solution for.

Code above actually will “add Attributes to the existing Express Object without re-creating it”.

I really am looking for a way to explicitly declare all of it in PHP and not by importing from an external file. As in, to declare it idempotently.

For example, I can do this when initially making the Express Object ala:

$object = Express::buildObject(‘test1’, ‘test1’, ‘Test1’);
$object->addAttribute(‘user_selector’, ‘Owner User’, ‘test_owner’);
$object->save();

But the proposed method looks to be substantially more complicated just because the intent is to add the Attribute after the Express Object has been made. Surely there’s a more simple way…?

Well, you can get the same results writing more PHP code if you want. It doesn’t matter how do you add it.

But you will probably have to do it by yourself, because it seems that Express::buildObject() is written in a way to only create fresh Express object, not use existing ones.

This is something someone helped me with a while back and I ended up with this…

	public function action_addnewcolumn()
	{
		$coltype = $_POST['coltype'];
		$colname = $_POST['colname'];
		$colhandle = $_POST['colhandle'];
		
		if (is_object(Express::getObjectByHandle('attendee'))) {
		 	$eObj = Express::getObjectByHandle('attendee');
		  	$cat = $eObj->getAttributeKeyCategory();
		  	$exists = $cat->getAttributeKeyByHandle($colhandle);
			if (!is_object($exists)) {
				$builder = $this->app->make(ObjectBuilder::class);
				$builder->setEntity($eObj);
				$builder->addAttribute($coltype, $colname, $colhandle);//, $colsettings);
				$builder->save();
				echo json_encode('handle-available');
			} else {
				echo json_encode('handle-unavailable');
			}
		}
		exit();		
	}

For me, this is called in the view.php file with AJAX.
You’ll need these in your controller.php file

use Express;
use Concrete\Core\Express\ObjectBuilder as CoreObjectBuilder;

And here’s the post I picked it up from…

Oh wow thanks! I think I see how that works… it kind of… re-inits the Builder functionality and then lets you work with it again! I suspect that may be unintended by the devs, but oh well!

I’ll have to try that when I get a chance :slight_smile: Again, thanks!