Jump to content

a-ok

Members
  • Posts

    812
  • Joined

  • Last visited

Posts posted by a-ok

  1. 2 minutes ago, kongondo said:

    Yes, it should. As long as you tell the JS what criteria to use to filter things. I don't know if you are using vanilla JS or some library but the principle is the same. You will filter out/in items to either remove/hide OR add/display depending on whether you filtered out or in. 

    This is how we do it in Menu Builder, giving us flexibility to create all sorts of menus.

    If you have more questions specific to your use case (including the JS), please open a new thread so that we stay on topic in this thread. Thanks.

    @kongondo Thanks! Using vanilla/alpine/react sort of thing but yeah principle should be the same ? 

    Okay great. Will be in touch if I have any more questions but super appreciate the help/advice.

  2. 2 hours ago, kongondo said:

    What's your use case? Deeply nested arrays can get unwieldy and depending on how big they are, consume a lot of memory.  I find it simpler to work with flat(ter) arrays with in-built relationships between the members. I then run that through the recursive function. Something like this:

    
    <?php namespace ProcessWire;
    
    $menu = array(
     1 => array(
      'id' => 1023,
      'title' => 'Menu item 1',
      'parent' => null, // this menu item does not have a parent/top level
     ),
     2 => array(
      'id' => 1024,
      'title' => 'Menu item 2',
      'parent' => null,
     ),
     3 => array(
      'id' => 3000,
      'title' => 'Child 1 of Menu item 2',
      'parent' => 1024, // its parent is item with ID 1024
     ),
     4 => array(
      'id' => 3001,
      'title' => 'Child 2 of Menu item 2',
      'parent' => 1024,
     ),
     5 => array(
      'id' => 3003,
      'title' => 'Child 1 of Child Menu item 2',
      'parent' => 3001,
     ),
     6 => array(
      'id' => 1027,
      'title' => 'Menu item 4',
      'parent' => null,
     ),
     7 => array(
      'id' => 3009,
      'title' => 'Child 1 of Menu item 4',
      'parent' => 1027,
     ),
     8 => array(
      'id' => 4001,
      'title' => 'Child 1 of Child 1 of Menu item 4',
      'parent' => 3009,
     ),
     9 => array(
      'id' => 4002,
      'title' => 'Child 2 of Child 1 of Menu item 4',
      'parent' => 3009,
     ),
    
    );

     

    @kongondo This is super interesting. My set up in PW is the following:

    • Works
      • Categories
        • Artworks
          • Digital
          • Painting
        • Books
        • Editions
      • Work
      • Work
      • Work
    • Activities
      • Categories
        • Events
          • Reading
          • Talks
          • Workshops
        • Exhibitions
          • Curated
          • Group
          • Solo
        • Publications
          • Anthologisations
          • Books
          • Editing
          • Essays
      • Activity
      • Activity
      • Activity

    And what I'm wanting to do on the front end (with JS) is when the user clicks 'Works' then it shows the first category level ('Artworks', 'Books', 'Editions'), then if the user clicks 'Artworks' it would show 'Digital' and 'Painting' while keeping the previous 'parents' active.

    I'll figure out the JS somehow for the menu but I'm guessing your method (which I think is a super smart way of doing it) would structurally allow for this sort of functionality?

  3. On 2/22/2013 at 9:06 PM, mindplay.dk said:

    This is so common - I do things like this all the time, not just for menus.

    Does anyone else think this should be part of the core?

    For now, I wrote a simple flat function:

    
    /**
     * Recursive traverse and visit every child in a sub-tree of Pages.
     *
     * @param Page $parent root Page from which to traverse
     * @param callable $enter function to call upon visiting a child Page
     * @param callable|null $exit function to call after visiting a child Page (and all of it's children)
     */
    function visit(Page $parent, $enter, $exit=null)
    {
        foreach ($parent->children() as $child) {
            call_user_func($enter, $child);
            
            if ($child->numChildren > 0) {
                visit($child, $enter, $exit);
            }
            
            if ($exit) {
                call_user_func($exit, $child);
            }
        }
    }

    With PHP 5.3 you can generate a menu (or whatever else) recursively as simple as this:

    
    visit(
        $pages->get('/menus/main')
        ,
        function(Page $page) {
            echo '<li><a href="' . $page->url . '">' . $page->title . '</a>';
            if ($page->numChildren > 0) {
                echo '<ul>';
            }
        }
        ,
        function(Page $page) {
            echo '</li>';
            if ($page->numChildren > 0) {
                echo '</ul>';
            }
        }
    );

    Dead simple.

    I've seen the options for modules etc. that generate menus - they seem to grow out of control with a million options, and my own helpers seem to evolve the same way, and it doesn't jive with the beautiful, self-contained, simple templates you normally get away with in PW.

    Would it make sense to have a standard visit() method in Page in the core?

    I realise this post is SUPER old but I'm after something similar that outputs/builds an array, rather than markup.

     Something like...

    menu => array(
    	main => array(
    		1023 => array(
    			id => 1023
    			title => 'Menu item 1',
    			children => null
    		),
    		1024 => array(
    			id => 1024
    			title => 'Menu item 1',
    			children => array(
    				3000 => array(
                      id => 3000
                      title => 'Child menu item 1',
                      children => null
    				),
    				3001 => array(
                      id => 3001
                      title => 'Child menu item 2',
                      children => null
    				)
    			)
    		),
    		1024 => array(
    			id => 1024
    			title => 'Menu item 1',
    			children => array(
    				3000 => array(
                    	id => 3000
                      	title => 'Child menu item 1',
                      	children => array(
    						4001 => array(
                              id => 4001
                              title => 'Child menu item 2',
                              children => null
                            ),
    						4002 => array(
                              id => 4002
                              title => 'Child menu item 2',
                              children => null
                            )
    					)
    				),
    				3001 => array(
                      id => 3001
                      title => 'Child menu item 2',
                      children => null
    				)
    			)
    		)
    	)
    )

     

  4. I'm attempting to build a dynamic filtering menu. I have two main templates ('activities' and 'works') and these all share the same children template 'singleA'.

    Work
      - Work a (singleA)
      - Work b (singleA)
    Activities
      - Activity a (singleA)
      - Activity b (singleA)

    Within each (singleA) template you can tag it with as many keywords as you wish. These keywords all share the same template 'keyword' and can have infinite children.

    Keywords
      - Event (keyword)
        - Talk (keyword)
      - Exhibition (keyword)
        - Solo (keyword)
        - Group (keyword)

    What I am wanting to achieve is when a user clicks on 'Work' in a menu, it'll show all the keywords with a depth of 1 that have 'works' (singleA) tagged. Then, when the user clicks the keyword 'Exhibition', for example, it'll show all the keywords, with a depth of 2 etc. I'm using @Robin S's `Connect Page Fields` for this. All the data is set up it's just I'm getting stuck trying to work out how dynamically build this as the depths could be 1, 2, 3 or 40 (unlikely but still keen to keep it dynamic).

    Any thoughts?

    $menuPages = $pages->find("template=activities|works");
    foreach ($menuPages as $menuPage) {
    
        $keywordsAll = $pages->find("template=keyword, singles.parent=$menuPage");
        foreach ($keywordsAll as $keyword) {
            
        }
    
    }

     

  5. Thanks @MoritzLost for the reply!
     

    The token is simply one token for the whole API that expires every 60 days. If you refresh the token, before it expires, it gives you a new token, otherwise you have to go through the pain in the ass Facebook OG process setting up a token again.

    The initI’ll token, which I get manually, has to be set first and after 30 days (to give the 60 days expiry some cushion!) it should run the refresh call and replace it. And so forth.

    I guess I could just set up the cache with the init API manually, without it being programmatic, and then just run the usual  expiry test but I thought it would be nice to have it work from the get go.

    Also I guess another issue is when it expires it needs to run the refresh call with the token that’s due to expire but at that point it will have already expired in the cache and therefore not exist...

     

  6. Regarding the `$cache` API I need to check whether the cache requested has either expired OR doesn't exist.

    Normally I just do this usual check which creates the cache if the get method returns null or doesn't exist:

    $result = wire('cache')->get('instagramFeed', 3600);
    if (!$result) {
    	$result = $instagramHttp->getJSON($instagramBase);
    	wire('cache')->save('instagramFeed', $result, 3600); // Expire every hour
    }

    I need to store an access token in the cache also, which expires every month. However, I need to do a check whether the cache is expired (and run a query to fetch and store a new one) OR if it simply didn't exist (which in theory would only be called once to initiate the cache) which would populate it with a static token.

    I read this in the docs:

    Quote

    If cache exists and is older, then blank returned. You may omit this to divert to whatever expiration was specified at save() time. Note: The $expire and $func arguments may optionally be reversed.

    Am I right in thinking if I call `wire('cache')->get('instagramFeed', 3600);` and the cache exists but is expired it wouldn't return null but rather an empty string? But if the cache doesn't exist then it returns null? And therefore I could do a check like so?

    $instagramHttp = new WireHttp();
    $instagramAccessToken = wire('cache')->get('instagramAccessToken', WireCache::expireMonthly);
    if (!$instagramAccessToken) { // null = doesn't exist = use static token
    	$instagramAccessToken = '123456789010';
    } else { // 'blank' assumed expired
    	$instagramAccessToken = $instagramHttp->getJSON("https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=$instagramAccessToken")
    }

     

  7. 14 hours ago, Robin S said:

    That way you are matching the pages that contain the repeater field so you don't need getForPage(), plus you don't have to deal with the access restrictions on the Repeater template, unpublished repeater items, sorting of the container pages, etc.

     

    Of course! That makes perfect sense. Doh.

    Your method of merging all the relevant text to one field makes sense, also. Thanks and nice work! The only issue is if someone searched 'Kettle's' it wouldn't return, would it, as the text it's returning doesn't have the apostrophe?

    Also, while I'm on the subject, has anyone attempted to implement a fuzzy search with PW?

  8. Yeah tried them all.

    $results = $pages->find("$fieldsToSearch%=$searchQuery, template=$templatesToSearch, sort=sort, check_access=0");
    
    $resultsToRemove = new PageArray();
    foreach ($results as $result) {
    	if ($result->template->name == 'repeater_modulesA' && ($result->getForPage()->is('unpublished') || $result->getForPage()->is('trash'))) {
    		$resultsToRemove->add($result);
    	}
    }
    $results = $results->not($resultsToRemove);
  9. $results = $pages->find("$fieldsToSearch%=$searchQuery, template=$templatesToSearch, sort=sort, check_access=0");
    
    $resultsToRemove = new PageArray();
    foreach ($results as $result) {
    	if ($result->template->name == 'repeater_modulesA' && ($result->getForPage()->is('unpublished') || $result->getForPage()->is('trash'))) {
    		$resultsToRemove->add($result);
    	}
    }
    $results = $results->not($resultsToRemove);

    I had to end up doing the above. Maybe this is correct but seems a bit crazy.

  10. I'm a bit stuck with this search result setup.

    $fieldsToSearch = 'title|text|textA|textarea|projectsCategories.title|projectsClients.title|aboutPressCategories.title|aboutPressClients.title';
    $templatesToSearch = 'projectsSingle|shopSingle|typeSingle|typeCustomSingle|typeCustomSingleB|aboutPressSingle|repeater_modulesA';
    
    $results = $pages->find("$fieldsToSearch%=$searchQuery, template=$templatesToSearch, sort=sort, check_access=0");

    It should search all those templates (including the repeater). If the result is a repeater then it returns the `getForPage()`

    foreach ($results as $result) {
    	if ($result->template->name == 'repeater_modulesA') $result = $result->getForPage();
    }

    This is pretty straightforward. I just have an issue...

    1. If the getForPage() is in the trash it's still getting returned (it shouldn't) but I guess that's because the repeater isn't in the trash.

    Is there anything I can apply to the selector to avoid this? Or before I loop through $results should I do a check to remove any $result where status is trash?

    $results = $pages->find("$fieldsToSearch%=$searchQuery, template=$templatesToSearch, sort=sort, check_access=0");
    $results = $results->not("status<" . Page::statusTrash);

    Thoughts? I guess the issue with the above is that it'll be checking the repeater for trash, not the getForPage().

  11. @Robin S

    Sorry to bring this back up.

    $fieldsToSearch = 'name|title|text|textA|textarea|projectsCategories.title|projectsClients.title|aboutPressCategories.title|aboutPressClients.title';
    
    $templatesToSearch = 'projectsSingle|shopSingle|typeSingle|typeCustomSingle|typeCustomSingleB|aboutPressSingle|repeater_modulesA';
    
    $results = $pages->find("$fieldsToSearch%=$searchQuery, template=$templatesToSearch, sort=sort, check_access=0");

    I am getting a `Operator '%=' is not implemented in FieldtypePage` error when searching for numbers, also. However, my Page Reference Fields should all be searching `.title`?

  12. On 5/15/2020 at 5:33 PM, Mike Rockett said:

    @a-ok Jumplinks 1 doesn't have built in support for automatically dealing with query strings. This is planned for Jumplinks 2, but I simply haven't had the time to complete it. For the time being, I'd be inclined to use the {all} wildcard on the end.

    Source: chance-to-dance[/]{all}
    Destination: somewhere/{all} or {!all} to skip wildcard cleaning

    Thanks for the help ?

    Does anyone have a solution for using Jumplinks with urlSegments?

    • Like 1
  13. I couldn't find an answer to this. Is it possible to declare both a time and if a template is saved to expire a cache?

    wire('cache')->save('instagramFeed', $result, 1800);
    wire('cache')->save('instagramFeed', $result, WireCache::expireDaily);
    wire('cache')->save('instagramFeed', $result, "template=home");

    Any thoughts?

  14. Just back to this, sorry.

    I've set up a template file which is outputting the CSS and all seems okay on that front BUT the file itself seems to take an age to load and thus makes the load time of the whole site super slow.

    Any thoughts if I'm implementing this wrong?

    Screenshot 2020-05-28 at 10.28.55.jpg

  15. I ended up with...

    $modules = $page->modulesA;
    
    if ($input->get('f')) {
        $moduleFilter = $input->get('f');
        $modulesWithFilter = $modules->find("projectsCategories.name={$moduleFilter}");
        $modulesWithoutFilter = $modules->removeItems($modulesWithFilter);
        $modules = $modulesWithFilter->add($modulesWithoutFilter);
    }

     

  16. Is it possible to sort a Wire/PageArray (I'm using the RepeaterMatrix) by a field's value? The order is defined by the order in the CMS but if I wanted to change that order, on the front-end, to follow a pageField selection is this possible?

    Each row has a pageField that allows the client to select a number of categories that defines that row's content. If the client selects 'Digital' for a row then is there a way, on the front end, to put all the rows with that category first (thus breaking the order)? I would then use a get request (?f=digital) to define when the order should change, but I'm just wondering if the sorting order change is possible.

    I could probably loop through the rows and remove/append but wondered if this was possible within a selector?

×
×
  • Create New...