Multisite - can't get $propagatable[] to work as expected

I am experimenting with the multisite trait in a custom model.

I have 2 sites.

My custom model has a name and a description column. (and id of course)

In my model with the multisite trait I have the lines:

protected $propagatableSync = true;
protected $propagatable = [‘name’];

When I create a record using the backend in one site, it gets copied to the other site as expected ($propagatableSync = true)

From my understanding, only the value of “name” should be saved to the copy, as name is in the $propagatable array.

But actually the value of “description” gets copied too.

What am I understanding/doing wrong?

Thank you

1 Like

It seems like you are using Laravel’s multisite trait to sync data between two sites, and you have set the $propagatableSync property to true and $propagatable array to ['name'] to only sync the name column between the sites.

However, you have noticed that the description column is also getting copied to the other site, which is not what you were expecting.

One possible explanation for this behavior is that the description column is being automatically filled with a default value when a new record is created, and this default value is being copied over to the other site along with the name column.

To confirm this, you can try creating a new record with an empty description column and see if only the name column gets synced between the sites.

Another possibility is that there is some other code or configuration in your application that is causing the description column to be synced even though it is not in the $propagatable array.

To troubleshoot this issue further, you can try debugging your code and checking if the description column is being modified or accessed anywhere else in your application, and if there are any other settings or configurations that may be affecting the syncing behavior.

I hope this helps you in resolving the issue you are facing with the multisite trait in your custom model.

The description shouldn’t get copied on subsequent updates. It will get used for the seed content, though.

“Sync mode” will create models for all sites and they need some content to use, so everything will be propagated one time only.

I have some concerns regarding “everything will be propagated”:

  1. In the meanrime I added a belongsTo (brand) relationship to my (bicycle) model. And the brand model has multisite and $propagatableSync = true too.
    When I now create a bicycle, all attributes get propagated as you said. Including brand_id. So in the database this results in propagated bicycle records with brand_id from a different site_id (the site_id on which I created the original record).
    Therefore I think it isn’t optimal, that everything gets propagated upon creation, as I now have relations between records in the database which don’t make sense (and which don’t show up in the backend because of different site_id).

What would be optimal here in my opinion, would be that if the foreign key of another multisite model gets propagated, the related record for the current site would be identified via site_root_id/site_id and the found record id would be saved in the propagated record for the site - and if no corresponding record is found the foreign key column gets left empty.

  1. But also with a field like description, I’d prefer to have control whether it gets propagated upon creation or not. Because multisite i guess often is used for record localisation/translation - and its much clearer to see /select which translations are still missing when a field is empty instead of holding a copy of some other locale. Especially when dealing with locales like en-US vs en-GB or fr-CA vs fr-FR.

I mean, the original translate plugin also didn’t initially copy the default language to all translations.

what do you and others think?

(to be honest, I see that this propagation topic can get very complicated - ie when validation comes into play and also for other relation types then just belongsTo)

To clarify one thing here, relations don’t propagate like attributes, instead they are shared, and are therefore always in sync.

I know what you mean, this is indeed a possible pain point. The Translate plugin currently checks if the values are equal to know if they should be updated across all locales. We could possibly do the same thing with Multisite. This would be classing an attribute as “untouched” or “untranslated”.

2 Likes

Just ran into the same problem, but this time even worse:

I added a published switch to my model. Now when I open a published record in the backend, switch to another site in order to propagate it to there, the resulting copy is immediately published too. But of course not translated yet. Wrong language content immediately visible on the website.

Maybe I am missing something or am on the wrong path? How to approach or solve this? Translating takes time. And many times the original records are published already.

(I still think, that also for first seeding it would be so much better, to only propagate the fields in $[propagatable[]. This would solve the published-problem and my point 2. from above in one shot (fields to translate, easy to recognize because of emptyness - and even more obviously important for lets say a product model with a price field, where prices differ across sites: If price gets propagated for seeding, I see a price later in the copy - and almost no chance to be sure, whether it has been already set for the site or is a nonsense copy. I already see me double and triple-checking all day ;-))

…any way to extend/overwrite something to interfere here so that in my installation $[propagatable[] gets respected also for seeding?

Thank you for reading & hopefully answering:)

ps: just tried it in the tailor demo blog: open a published blog post, switch sites, say yes to record creation → copy is published immediately. I was hoping that the copy would be in disabled/hidden state as the post obviously needs to get translated before being published on the website… Many others must have run into that problem, no? Or am I really missing something?

Hey @chris5000

If we made replicated models unpublished by default, would that help?

1 Like

Yes, of course. For tailor this would help a lot. This would be column is_enabled=false, right?

For custom models not, as there is no standard column for that, as far as I know. So here adhering to $propagatable[] already upon record creation would be the easiest solution i guess (so one can have a field like is_published with default false, don’t have it in $propagatable and multisite propagation would omit it from the insert)

Actually yesterday I asked chatgpt to help me to make my custom model propagation work like that. With a customMultisite Trait:

<?php 

namespace Kinderradfinder\BicyclesCatalogue\Traits;

use site;

trait CustomMultisite
{
    /**
     * Find an existing model for the specified site or create a new one,
     * setting only the propagatable attributes for new models.
     *
     * @param mixed $siteId The site ID for which the model should be found or created.
     * @return Model The model instance for the specified site.
     */
    protected function findOtherSiteModel($siteId = null)
    {
        if ($siteId === null) {
            $siteId = $this->getSiteIdFromContext();
        }
    
        if ($this->isModelUsingSameSite($siteId)) {
            return $this;
        }
    
        $otherModel = $this->findForSite($siteId);
    
        // If a model for the other site doesn't exist, replicate the current model
        // with only the necessary attributes.
        if (!$otherModel) {
            $replicated = $this->replicateWithRelations();
    
            // Define the attributes that should always be maintained
            $alwaysKeep = ['created_at', 'deleted_at', 'updated_at', 'site_id', 'site_root_id', 'sort_order'];
    
            // Get all attribute keys from the model
            $attributes = array_keys($this->getAttributes());
    
            // Determine which attributes should be unset
            $attributesToUnset = array_filter($attributes, function ($attributeKey) use ($alwaysKeep) {
                return !in_array($attributeKey, $this->propagatable) &&
                       !in_array($attributeKey, $alwaysKeep) &&
                       substr($attributeKey, -3) !== '_id';
            });
    
            // Unset non-propagatable attributes, except for those ending with '_id'
            foreach ($attributesToUnset as $attributeKey) {
                unset($replicated->{$attributeKey});
            }
    
            $otherModel = $replicated;
            $otherModel->{$this->getSiteIdColumn()} = $siteId;
            $otherModel->site_root_id = $this->site_root_id ?: $this->id;
        }
    
        return $otherModel;
    }
  

    /**
     * Get the current site ID from the application context.
     *
     * @return mixed The current site ID.
     */
    protected function getSiteIdFromContext()
    {
        // Implement logic to retrieve the current site ID from the application context.
        // This is a placeholder and needs to be adapted based on your application's context management.
        return Site::getSiteIdFromContext();
    }
}
?>

…which I then use alongside the standard multisite trait in my model:

use October\Rain\Database\Traits\Multisite as OriginalMultisite;
use Kinderradfinder\BicyclesCatalogue\Traits\CustomMultisite;

use OriginalMultisite, CustomMultisite {
        CustomMultisite::findOtherSiteModel insteadof OriginalMultisite; // Ensure CustomMultisite's version is used.
    }

…probably not nice, but it seems to work for my model. But Tailor is_enabled is another story, where not me nor chatgpt had an idea how to cleanly interfere.

So, yes: If you made replicated models unpublished by default, that would definitely help a lot!

1 Like