Jump to content

a-ok

Members
  • Posts

    808
  • Joined

  • Last visited

Posts posted by a-ok

  1. 16 hours ago, elabx said:

    Have you tried PHP's get_headers?

    What's wrong with the way you are doing it now? I understand from your replies that it is actually working?

    Also, the request's JSON seems to have a topic field that you can check too, in case you want to skip the header check.

    Yes but I thought the headers `X-Shopify-Topic` etc would be included in the response. but it's not? `file_get_contents("php://input")` returns the raw data, which is good, but no way of checking the headers on this? Sorry I'm just a bit confused.

  2. I used `php://input' to test with and it's returning the data from the Shopify hook of the product updated etc.

    $test = json_decode(file_get_contents("php://input"), true);

    Is there any way to do this using the PW API and if not is it possible to get the headers so I know what X-Shopify-Topic is being requested?

  3. @elabx 4 years ago you posted this and it's still tripping me up!

    I now have a Shopify webhook firing to /api/ in my PW app when a product is updated. I have verified that PW is getting the response with the following...

    $log->save('shopifyApi', 'Webhook triggered from Shopify');

    Great! The request is a POST request so I'm assuming I use `$input->post()` but I am unsure how to check the headers to see what the X-Shopify-Topic is and also to grab the data?

    Attached below is the webhook response tested on request catcher...

    Screenshot 2021-08-04 at 12.06.07@2x.jpg

  4. I've added a page hook method (addHookMethod) in ready.php and I am calling it on a page. Simple enough. See below for the current code.

    I now need to pass parameters into the hook but can't figure out how this is possible?

    So with the current usage here:

    $product = $page->shopifyGetProductById();

    I'd like to pass some parameters into the hook to then use within the hook function itself:

    $product = $page->shopifyGetProductById($data);
    wire()->addHookMethod('Page::shopifyGetProductById', function($event) {
    	$page = $event->object;
    
    	$gid = "gid://shopify/Product/{$page->shopifyProductId}";
    	$query = <<<GQL
    	query {
    	  product(id: "$gid") {
    		title
    	  }
    	}
    	GQL;
    
    	$result = wire('cache')->get("shopifyGetProductById{$page->shopifyProductId}");
    	if (!$result) {
    		$result = shopifyGQL($query);
    		wire('cache')->save("shopifyGetProductById{$page->shopifyProductId}", $result, "template=shopifyProduct, shopifyProductId={$page->shopifyProductId}");
    	}
    	$event->return = $result;
    });

     

  5. I tend to use $cache to cache JSON in the database, which is great, but I need to return HTML markup from a JS fetch and I'm unsure how to use PW to return cached HTML.

    Below is how I do it for JSON but could anyone lend a hand for HTML?

    $segments = array($input->urlSegment1, $input->urlSegment2, $input->urlSegment3);
    
    $data = array();
    
    if ($segments[0] === 'v1' && $segments[1] === 'portfolio') {
    
    	$response = $cache->get('apiPortfolio');
    	if (!$response) {
    		$portfolio = array();
    		$projects = $pages->find("template=project, sort=-date");
    		if (count($projects)) {
    			foreach ($projects as $project) {
    				$portfolio[] = array(
    					'id' => $project->id,
    					'title' => $project->title
    				);
    			}
    		}
    		$response = $portfolio;
    		$cache->save('apiPortfolio', $response, "template=portfolio");
    	}
    	$data['response'] = $response;
    
    } else {
    	header('Content-Type: text/html');
    	throw new Wire404Exception();
    }
    
    $dataJson = wireEncodeJSON($data);
    
    header('Content-Type: application/json');
    echo $dataJson;

     

  6. 3 minutes ago, dragan said:

    Well, if you have the budget, you could use one of the commercial services out there which have an API.

    This article suggests using Chrome in headless mode, but I have no idea how difficult it is to install, and what the server requirements are:

    Thanks @dragan – I tried to find some decent APIs but couldn't find anything decent... are you aware of any?

    And yes I was looking at the headless Chrome stuff too but was quickly confused haha!

  7. Is there a modern way to create PDF on the fly these days with the data from PW? mPDF seems fairly old school and only supports tables etc but was looking for something that supports flexbox/css grid etc. I could just use a print stylesheet but it needs to download a PDF, ideally, on click.

    Any thoughts?

    I've had a look at RockPdf and Pages2Pdf but wondered if there was anything else?

    Thanks!

    Rich

    • Like 1
  8. I ended up using Guzzle to do, for example, the following:

    // Shopify init
    function shopifyQL($query) {
    
    	$shopifyHost = "xxxxx.myshopify.com";
    	$shopifyPassword = "xxxxx";
    	$graphqlEndpoint = "https://$shopifyHost/admin/api/2021-01/graphql.json";
    	$client = new \GuzzleHttp\Client();
    
    	$response = $client->request('POST', $graphqlEndpoint, [
    		'headers' => [
    			'Content-Type' => 'application/json',
    			'X-Shopify-Access-Token' => $shopifyPassword,
    			'X-Shopify-Api-Features' => 'include-presentment-prices'
    		],
    		'json' => [
    			'query' => $query
    		]
    	]);
    
    	$json = $response->getBody()->getContents();
    	$body = json_decode($json, true);
    	$data = $body['data'];
    	return $data;
    
    }
    
    // Get Shopify shop defaults (useful for currency checks)
    wire()->addHookMethod('Page::shopifyShop', function($event) {
    	$page = $event->object;
    
    	$query = <<<GQL
    	query {
    	  shop {
    		name
    		primaryDomain {
    		  url
    		  host
    		}
    	  }
    	}
    	GQL;
    
    	// Get cache only if it's less than or equal to 1 month
    	$result = wire('cache')->get('shopifyShop', WireCache::expireMonthly);
    	if (!$result) {
    		$result = shopifyQL($query);
    		wire('cache')->save('shopifyShop', $result, WireCache::expireMonthly);
    	}
    	$event->return = $result;
    });

     

    • Like 1
  9. I've been having a play... and I'm getting somewhere but passing the query itself is something I'm struggling with. I might end up using Guzzle but thought I'd see if it was possible using PW natively.

    // Shopify init
    $shopifyHttp = new WireHttp();
    $shopifyHost = "xxxx.myshopify.com";
    $shopifyPassword = "xxxx";
    $shopifyHttp->setHeaders([
    	'Content-Type' => 'application/json',
    	'X-Shopify-Access-Token' => $shopifyPassword,
    	'X-Shopify-Api-Features' => 'include-presentment-prices'
    ]);
    $shopifyBase = "https://$shopifyHost/admin/api/2021-01/graphql.json";
    
    $query = <<<GQL
    query {
    	shop {
    		name
    			primaryDomain {
    				url
    				host
    			}
    		}
    	}
    }
    GQL;
    
    bdb($shopifyHttp->post($shopifyBase, [
    	'query' => json_decode($query)
    ]));

    Any thoughts? I've tested the POST request in Postman and works and this was the PHP HTTP code for that...

    <?php
    require_once 'HTTP/Request2.php';
    $request = new HTTP_Request2();
    $request->setUrl('https://xxxxx.myshopify.com/admin/api/2021-01/graphql.json');
    $request->setMethod(HTTP_Request2::METHOD_POST);
    $request->setConfig(array(
      'follow_redirects' => TRUE
    ));
    $request->setHeader(array(
      'X-Shopify-Access-Token' => 'xxxxx',
      'Content-Type' => 'application/json',
      'Cookie' => '_shopify_fs=2021-03-31T10%3A14%3A46Z; _y=82249b91-c91c-4631-8cb9-2a859e3e756e; _s=5be7df5e-f9a6-45a9-a472-e7f262f54a8b; _shopify_y=82249b91-c91c-4631-8cb9-2a859e3e756e; _shopify_s=5be7df5e-f9a6-45a9-a472-e7f262f54a8b'
    ));
    $request->setBody('{"query":"  query {\\n    shop {\\n      name\\n      primaryDomain {\\n        url\\n        host\\n      }\\n    }\\n  }","variables":{}}');
    try {
      $response = $request->send();
      if ($response->getStatus() == 200) {
        echo $response->getBody();
      }
      else {
        echo 'Unexpected HTTP status: ' . $response->getStatus() . ' ' .
        $response->getReasonPhrase();
      }
    }
    catch(HTTP_Request2_Exception $e) {
      echo 'Error: ' . $e->getMessage();
    }

     

  10. Thanks so much for this,  @teppo 🙂

    Quote

    So you are looking into querying data from an external service (Shopify?) that uses GraphQL?

    Yep, exactly. Currently I do something like the following in ready.php  – I do something similar for products etc.

    // Shopify init
    $shopifyHttp = new WireHttp();
    $shopifyHttp->setHeaders(['X-Shopify-Api-Features' => 'include-presentment-prices']);
    $shopifyHost = "xxxxxx.myshopify.com";
    $shopifyAPIKey = "xxxxxx";
    $shopifyPassword = "xxxxxx";
    $shopifyBase = "https://$shopifyAPIKey:$shopifyPassword@$shopifyHost/admin/";
    
    // Get Shopify shop defaults (useful for currency checks)
    wire()->addHookMethod("Page::shopifyShop", function($event) use($shopifyHttp, $shopifyBase) {
    	$page = $event->object;
    	// Get cache only if it's less than or equal to 1 month
    	$result = wire("cache")->get("shopifyShop", WireCache::expireMonthly);
    	if (!$result) {
    		$result = $shopifyHttp->getJSON($shopifyBase . "shop.json");
    		wire("cache")->save("shopifyShop", $result, WireCache::expireMonthly);
    	}
    	$event->return = $result;
    });

    This works great BUT the Shopify API is moving closer to GraphQL each day and feel like I should adapt, or die 😉

    I'll look at those examples and see how I could change this up!

     

     

  11. Hi folks. I’m a bit confused.

    I built a site a year or so ago that uses `wireHttp()` to query Shopify endpoints and then would store this in the `wireCache` as well create pages on PW based on products returned etc. It was all quite simple but worked well.

    Anyway. I’m looking to replace this set up with GraphQL but I’m getting super confused. Is GraphQL a replacement for wireHttp as it’s a REST replacement? Can you use GraphQL on Processwire to return third party data?

    Any advice or help understanding would be really appreciated.

  12. I was using MAMP for local development for years and Tracy never had any issues.

    I've recently just switched to using Laravel Valet with the custom driver and all seems okay but when I enable Tracy I get the following error.

    When I disable Tracey and debug `$_SERVER['REQUEST_URI']` it returns false.

    Any thoughts on how I can continue to use Tracy (which is so brilliant useful) when using Laravel Valet?

    Thanks!

    Screenshot 2021-02-08 at 13.55.58.jpg

  13. Hi folks,

    I've been moving away from a local set up of MAMP to Laravel Valet, which all seems okay. I'm using the PW driver too. I'm assuming that's working ok?

    Only issue I seem to have is with two modules:

    Field: Fieldtype 'FieldtypeRepeaterMatrix' does not exist
    Field: Fieldtype 'FieldtypeFieldsetGroup' does not exist

    These were installed and in use on my previous set up (MAMP) and the files are all there in the site/modules/ folder but for some reason they're not getting picked up?

    I've tried refreshing the modules as well as clearing the compiled files but no luck.

    Any immediate thoughts?

    Thanks!

  14. I believe the error was in fact the use of `->not()` as I was supply a PageArray to remove and not a selector?

    I simply changed this to the below which seems to have the same effect without the error?

    foreach ($results as $result) {
    	if ($result->template->name == 'repeater_modulesA' && ($result->getForPage()->is('unpublished') || $result->getForPage()->is('trash'))) {
    		$results->remove($result);
    	}
    }

     

  15. I'm using the selector operator %= when returning search results:

    $searchQuery = $sanitizer->text($input->get->q);
    $searchQuery = $sanitizer->selectorValue($searchQuery);
    $input->whitelist('q', $searchQuery);
    
    $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");

    However, I'm getting an error:

    ProcessWire\WireException Unknown Selector operator:  -- was your selector value properly escaped? field=, value=, selector:

    If I change the operator from %= to = then no error is returned but obviously this isn't the ideal operator for what I'm doing.

    However, to create even more mystery, this error seems only to be returned when using certain words. For example 'house' will return the error but 'houses' won't.

    So now I'm thinking the error is actually not related to the above but actually to what follows...

    $resultsToRemove = new PageArray();
    foreach ($results as $result) {
    	if ($result->template->name == 'repeater_modulesA' && ($result->getForPage()->is('unpublished') || $result->getForPage()->is('trash'))) {
    		$resultsToRemove->add($result);
    	}
    }
    
    if (count($resultsToRemove)) $results = $results->not($resultsToRemove);
    
    $resultCount = count($results);

    This part is required as I am searching repeater templates using check_access=0 but obviously don't want unpublished repeater rows or trashed repeaters to be returned so I need to do a little check. I think this is maybe where it's breaking but can't know for sure. I'm also unsure why it would break for the search query 'house' but not 'houses'?

    If I can provide any more info to help me resolve this I can do... just a bit stumped.

     

    Screenshot 2020-12-08 at 09.32.46.jpg

  16. 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.

  17. 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?

  18. 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
    				)
    			)
    		)
    	)
    )

     

×
×
  • Create New...