Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pages module #126

Closed
zipavlin opened this issue Dec 18, 2018 · 17 comments
Closed

Pages module #126

zipavlin opened this issue Dec 18, 2018 · 17 comments
Labels
type: documentation This issue means we need to improve docs

Comments

@zipavlin
Copy link
Contributor

First of, thanks for sharing your work, it looks amazing!

I am trying to create a pages module, but I am not sure how to go about it.

Documentation states that 'Pages are unstructured data models most often used for static content, such as an About page [...]', which I understand should not be prefixed with 'pages'. I also don't know how to arrange it in page/subpage/... convention. Do you maybe have any more detailed docs about it?

I will also need pages to have different page templates with different fields. I read #54 and will try to implement it somehow, but I would appreciate if you have any ideas how to do that or where to start?

Also related: is it/or would be possible to implement a sort of addons system. For example a 'pages' addon that would scaffold all of this (tree structure, templates, ...) and leave user to populate template' fields, because it seems like something that a lot of projects requiring CMS would actually need.

Thanks for your help!

@noxify
Copy link

noxify commented Dec 18, 2018

I will also need pages to have different page templates with different fields. I read #54 and will try to implement it somehow, but I would appreciate if you have any ideas how to do that or where to start?

You could add a field template or type to your pages tables.
This field contains the information about the view which should be shown.

I tested it quickly (without any magic), here the snippet:
https://gist.github.com/noxify/69f3b1d41f3b338f77c1dfb2008f878a

Documentation states that 'Pages are unstructured data models most often used for static content, such as an About page [...]', which I understand should not be prefixed with 'pages'. I also don't know how to arrange it in page/subpage/... convention. Do you maybe have any more detailed docs about it?

Maybe you could disable the permalink functionality via

    protected $indexOptions = [
        'permalink' => false
    ];

in your controller, then you can implement your own logic (Route/Controller etc.) for the frontend.

Alternative you can remove the module name from the url via

    protected $permalinkBase = '';

Hope that helps :)

Greets!

@zipavlin
Copy link
Contributor Author

zipavlin commented Dec 19, 2018

Thank you @noxify the gists works perfectly! Now, I just need to figure how to make block(s) not flexible (so I don't need to have pages table with 50 columns) and I have everything :)

Your solutions for pages prefix doesn't work in my case, but I might need to try again, as there was no change after implementing either or both of those ($indexOptions, $permalinkBase).
EDIT: ok, I just re-read your answer and I noticed, that I should do this in controller not in model. Will try again, when I get back to office.

Thanks again for your help, you are awesome! 👍

@ifox ifox added the question label Dec 19, 2018
@zipavlin
Copy link
Contributor Author

EDIT: ok, I just re-read your answer and I noticed, that I should do this in controller not in model. Will try again, when I get back to office.

Using

 protected $permalinkBase = '';

works as expected!

@ifox
Copy link
Member

ifox commented Jan 2, 2019

Thanks @noxify for providing an example! The only thing I would do differently to avoid overriding the edit action in the admin controller would be to load the Blade template corresponding to the template attribute from the form.blade.php file of the module. The record is accessible using $item, so something like this:

@extends('twill::layouts.form')

@include('admin.pages.' . $item->template)

@zipavlin thanks for your kind words! Docs around managing stand-alone and parent/child kind of pages could definitely be improved, so let's use this conversation to start working on it.

#54 should help for unique (usually seeded) or template based pages but not regarding manageable parent/child pages. I'd like to integrate this in Twill in the future but right now this means manually integrating a model in your app with the laravel-nestedset package.

To install the package: composer require kalnoy/nestedset

Then add nested set columns to your database table:

For Laravel 5.5 and above users:

Schema::create('pages', function (Blueprint $table) {
    ...
    $table->nestedSet();
});

// To drop columns
Schema::table('pages', function (Blueprint $table) {
    $table->dropNestedSet();
});

For prior Laravel versions:

...
use Kalnoy\Nestedset\NestedSet;

Schema::create('pages', function (Blueprint $table) {
    ...
    NestedSet::columns($table);
});

// To drop columns
Schema::table('pages', function (Blueprint $table) {
    NestedSet::dropColumns($table);
});

Your model should use Kalnoy\Nestedset\NodeTrait trait to enable nested sets, as well as the HasPosition trait and some helper functions to save a new tree organisation from Twill's drag and drop UI:

use A17\Twill\Models\Behaviors\HasPosition;
use Kalnoy\Nestedset\NodeTrait;
...

class Page extends Model {
    use HasPostion, NodeTrait;
    ...
    public static function saveTreeFromIds($nodesArray)
    {
        $parentNodes = self::find(array_pluck($nodesArray, 'id'));
    
        if (is_array($nodesArray)) {
            $position = 1;
            foreach ($nodesArray as $nodeArray) {
                $node = $parentNodes->where('id', $nodeArray['id'])->first();
                $node->position = $position++;
                $node->saveAsRoot();
            }
        }
    
        $parentNodes = self::find(array_pluck($nodesArray, 'id'));
    
        self::rebuildTree($nodesArray, $parentNodes);
    }
    
    public static function rebuildTree($nodesArray, $parentNodes)
    {
        if (is_array($nodesArray)) {
            foreach ($nodesArray as $nodeArray) {
                $parent = $parentNodes->where('id', $nodeArray['id'])->first();
                if (isset($nodeArray['children']) && is_array($nodeArray['children'])) {
                    $position = 1;
                    $nodes = self::find(array_pluck($nodeArray['children'], 'id'));
                    foreach ($nodeArray['children'] as $child) {
                        //append the children to their (old/new)parents
                        $descendant = $nodes->where('id', $child['id'])->first();
                        $descendant->position = $position++;
                        $descendant->parent_id = $parent->id;
                        $descendant->save();
                        self::rebuildTree($nodeArray['children'], $nodes);
                    }
                }
            }
        }
    }
}

From your module's repository, you'll need to override the setNewOrder function:

public function setNewOrder($ids)
{
    DB::transaction(function () use ($ids) {
        Page::saveTreeFromIds($ids);
    }, 3);
}

If you expect your users to create a lot of records, you'll want to move this operation into a queued job.

Finally, to enable Twill's nested listing UI, you'll need to do the following in your module's controller:

protected $indexOptions = [
    'reorder' => true,
];

protected function indexData($request)
{
    return [
        'nested' => true,
        'nestedDepth' => 2, // this controls the allowed depth in UI
    ];
}

protected function transformIndexItems($items)
{
    return $items->toTree();
}

protected function indexItemData($item)
{
    return ($item->children ? [
        'children' => $this->getIndexTableData($item->children),
    ] : []);
}

Let me know if you try this and something goes wrong, I'm happy to help!

I'm definitely with you on the fact that this is a very common use case for a CMS and we are very interested in providing a way to create some kind of addons or predefined modules as well in the future.

@ifox ifox added the type: documentation This issue means we need to improve docs label Jan 2, 2019
@zipavlin
Copy link
Contributor Author

zipavlin commented Jan 8, 2019

@ifox thank you for your extended explanation and sorry for my late reply.

I changed the structure now, so I no longer need nesting pages, but I definitely will at some point in some other project, so I will check it at that time. It seems this issue is now solved, so I will close this issue.

On the other topic: I saw quite some issues have label 'need docs'. Maybe it would make sense to enable contribution to docs? Or something like 'recipes/guides' in docs? I keep finding new possibilities as I am going trough your code base :)

@zipavlin zipavlin closed this as completed Jan 8, 2019
@ifox
Copy link
Member

ifox commented Jan 8, 2019

Hi @zipavlin, sounds good!

Docs are open to contributions, the docs folder is a Vuepress project 🤓

Like I just mentioned on #46 (comment), a guides section would definitely makes sense, great suggestion!

@zipavlin
Copy link
Contributor Author

zipavlin commented Jan 9, 2019

Docs are open to contributions, the docs folder is a Vuepress project 🤓

Great, thanks! 👍

@frixxx
Copy link

frixxx commented Mar 14, 2019

Has anyone used this nested module stuff? Is there a way to get nested slugs?

F.e. i have the following items:

  • Item A - Nesting Depth 1
    • Item B - Nesting Depth 2

And now i want to have the slug /en/item-a/item-b) for the Item B and /en/item-a for the Item A.
Did anyone do this? Maybe someone could help?

@amirakbulut
Copy link

Has anyone used this nested module stuff? Is there a way to get nested slugs?

F.e. i have the following items:

  • Item A - Nesting Depth 1

    • Item B - Nesting Depth 2

And now i want to have the slug /en/item-a/item-b) for the Item B and /en/item-a for the Item A.
Did anyone do this? Maybe someone could help?

Have you solved this yet?

@humaxi
Copy link

humaxi commented Sep 26, 2019

Has anyone used this nested module stuff? Is there a way to get nested slugs?
F.e. i have the following items:

  • Item A - Nesting Depth 1

    • Item B - Nesting Depth 2

And now i want to have the slug /en/item-a/item-b) for the Item B and /en/item-a for the Item A.
Did anyone do this? Maybe someone could help?

Have you solved this yet?

Have you solved this yet?

@layout-lab
Copy link

Has anyone used this nested module stuff? Is there a way to get nested slugs?
F.e. i have the following items:

  • Item A - Nesting Depth 1

    • Item B - Nesting Depth 2

And now i want to have the slug /en/item-a/item-b) for the Item B and /en/item-a for the Item A.
Did anyone do this? Maybe someone could help?

Have you solved this yet?

Have you solved this yet?

Some time has passed; not sure if anyone else has any input on how to get the nested slugs to work?

@zipavlin
Copy link
Contributor Author

@layout-lab I recently needed pages (nested models). I solved nested slug problem by splitting url path and traversing upstream from last url slug towards its parents. I also rewrote Twill's slug prefix writing to include parent pages. It still needs some work, but I am happy to share my solution when I will be done (I hope I will remember about it).

@omzy
Copy link

omzy commented Aug 7, 2020

Personally I think this is the killer feature missing in Twill. I know there are workarounds but from my experience it's not perfect (see here: https://spectrum.chat/twill/general/subpage-permalinks~e3c1932b-6a6c-4221-a5a9-70e6b99bf96d). I think there should be native support for multi-level pages and dynamic permalinks built in to Twill.

Often clients just want a quick and straightforward way of adding pages in a parent-child hierarchy, and therefore I tend to lean towards WordPress for that.

Can we expect to have this feature anytime soon?

@mikerockett
Copy link
Contributor

I'm really surprised that Twill doesn't have nested slug support… @ifox Is this planned at all? Is there a proper workaround?

@ifox
Copy link
Member

ifox commented Sep 1, 2021

Hey there, I didn't realize there was so much feedback around this missing. The nested-set package has all the functionality embedded to render the tree of pages as needed on the frontend, since each page generates its own slug. The permalink field and link can also be adjusted to render parent pages slugs as the prefix. It is not the most straightforward without better documentation. Our goal is to remove all this manual setup the documentation is currently having you go through and make the feature more robust and first-party in Twill. All that to say this is all possible and I'd love to share a demo that does it but I don't have that at hand right now, at least not something I could easily share. @pboivin and I can work on this soon, we'll keep this thread updated @mikerockett.

@mikerockett
Copy link
Contributor

@ifox – thanks so much, looking forward to it! I'll try a manual implementation for the minute.

@rakeshmali
Copy link

Hi @ifox is this page nesting feature now available? Could you please provide steps to prepare nested pages for frontend?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation This issue means we need to improve docs
Projects
None yet
Development

No branches or pull requests

10 participants