Skip to content

Value and object on assign #108

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

Open
asacarter opened this issue Nov 16, 2018 · 8 comments
Open

Value and object on assign #108

asacarter opened this issue Nov 16, 2018 · 8 comments

Comments

@asacarter
Copy link

asacarter commented Nov 16, 2018

Shopify is using a value and an object/array on their template object. Any idea how I can achieve something similar?

https://help.shopify.com/en/themes/liquid/objects/template

If you're on the product.alternate.liquid template:

{{ template }} will output product.alternate
{{ template.name }} will output product

@asacarter
Copy link
Author

asacarter commented Nov 17, 2018

I've been thinking about how it could be implemented. If an item is an array, it could check for an item in the array with the same key. E.g.

$assigns = array (
  'template' => array (
      'directory' => '',
      'template' => 'product.alternate',
      'name' => 'product',
      'suffix' => ''
   )
);

Then {{ template }} could be a short way of calling {{ template.template }}

What do you think?

As a quick fix in my code at the moment, I'm explicitly checking for template in the Variable method of the Context class.

So far I haven't been able to figure out how to check if the key is an array?

if ($key == 'template') { // should instead check if $key is an array
   $key = $key . '.' . $key;
}

@sanmai
Copy link
Collaborator

sanmai commented Nov 22, 2018

That calls for an ArrayAccess-implementing object, as far as I see.

@funkjedi
Copy link

funkjedi commented May 10, 2019

You can do this fairly easily using a Drop instead of an array. Here's quick example of what that might look like.

$assigns = [
    'template' => new TemplateDrop('templates/product.alternate.liquid')
];
<?php

use Illuminate\Support\Str;
use Liquid\Drop;

class TemplateDrop extends Drop
{
    public function __construct(string $templatePath)
    {
        $templatePath = preg_replace('/\.liquid$/', '', $templatePath);

        $this->liquid = [
            'name'      => basename($templatePath),
            'type'      => Str::before($templatePath, '.'),
            'suffix'    => null,
            'directory' => Str::contains($templatePath, '/') ? dirname($templatePath) : null,
        ];

        if (Str::contains($this->liquid['name'], '.')) {
            $this->liquid['suffix'] = Str::after($this->liquid['name'], '.');
        }
    }

    protected function beforeMethod($method)
    {
        return $this->liquid[$method] ?? null;
    }

    public function __toString()
    {
        return $this->liquid['name'];
    }
}

@funkjedi
Copy link

I didn’t see the Drop class. Can that also be used to lazy load database queries for things like blog posts? So the SQL queries don’t execute unless the blog object is parsed in a template?

Here's an example of a dynamic drop similar to Shopify linklists drop.

{% assign menu = linklists.main-menu %}

<h1>{{ menu.title }}
{% for link in menu.links %}
   <a href="{{ link.url }}">{{ link.title }}</a>
{% endfor %}
$assigns = [
    'linklists' => new LinkListsDrop
];
class LinkListsDrop extends \Liquid\Drop
{
    public function invokeDrop($handle)
    {
         if ($linklist = \App\Models\LinkList::where('handle', $handle)->first()) {
             return $linklist->toArray();
	}
    }
}

@asacarter
Copy link
Author

You can do this fairly easily using a Drop instead of an array. Here's quick example of what that might look like.

I'm not able to access elements in the elements in template.

{{ template }} works fine but {{ template.directory}} doesn't output anything.

Here are the assigns:

Array (
  [page_title] => Search results
  [page_description] =>
  [template] => Typeflex\TemplateDrop Object (
    [context:protected] =>
    [liquid] => Array (
      [name] => search
      [suffix] =>
      [directory] => templates
    )
  )
)

@funkjedi
Copy link

Ya sorry left this out. You'll need to overwrite the beforeMethod method like so.

class TemplateDrop extends \Liquid\Drop {
	protected function beforeMethod($method) {
		return $this->liquid[$method] ?? null;
	}
}

@asacarter
Copy link
Author

Ya sorry left this out. You'll need to overwrite the beforeMethod method like so.

Thanks, that has fixed it. 👍

I've created a Drop class to get the search types which in Shopify returns an array but also a string.

https://help.shopify.com/en/themes/liquid/objects/search#search-types

I can't get it to work with numeric arrays.

This works:

{{ search.types }}

This does not work:

{% for type in search.types %}
	{{ type }}
{% endfor %}

What am I doing wrong here?

use Liquid\Drop;

class SearchTypesDrop extends \Liquid\Drop
{
	public function __construct (string $search_types) {

		$types = array ();
		$valid_types = array ('article', 'page', 'document');
		
		if (!$search_types) {
			$types = $valid_types;
		} else { 
			$type_parts = explode (',', $search_types);
			foreach ($type_parts as $type_part) {
				$type_part = trim ($type_part);
				if (in_array ($type_part, $valid_types)) {
					$types[] = $type_part;
				}
			}

			if (!count ($types)) {
				$types = $valid_types;
			}
		}

		$this->liquid['types'] = $types;
	}

	protected function beforeMethod ($method) {
		return $this->liquid[$method] ?? null;
	}

	public function __toString () {
		return implode ('', $this->liquid['types']);
	}
}

The assigns:

Array (
  [search] => Array  (
    [performed] => 1
    [results_count] => 0
    [terms] => 0
    [results] => Array (
    )  
    [types] => Typeflex\SearchTypesDrop Object (
      [context:protected] =>
      [liquid] => Array (
        [types] => Array (
          [0] => document
          [1] => page
        )
      )
    ) 
  )
)

@funkjedi
Copy link

If you want to iterate (ie. foreach) the drop you'll need to make it iterable. For example:

class SearchTypesDrop extends \Liquid\Drop implements \IteratorAggregate
{
	protected $types = [];
	protected $validTypes = ['article', 'document', 'page'];

	public function __construct (string $types)
	{
		$types = explode(',', $types);
		$types = array_map('trim', $types);

		$this->types = array_intersect($this->validTypes, $types);

		if (count($this->types) === 0) {
			$this->types = $this->validTypes;
		}
	}

	public function getIterator()
	{
		return new \ArrayIterator($this->types);
	}

	public function __toString()
	{
		return implode('', $this->types);
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants