etc_query

3 July 2012, 11:36

Keywords: data, import, query, transform, calculate, dom, xpath, xml

This multipurpose Textpattern plugin can import and transform data from various sources: websites, xml or json feeds, databases, global variables and – most importantly – from chunks of html code, like the output of txp tags. It can also perform arithmetic or other calculations over imported data.

Contents

  1. Let us do some arithmetic.
  2. An alternative way
  3. So, what are these fancy {braces}?
  4. What is behind the scene?
  5. The typical job for etc_query.
  6. What else?

Let us do some arithmetic.

Our first example is a mere “two times two”: 2x2=<txp:etc_query query="2*2" /> will produce 2×2=4, pretty trivial, I know. But we can do it with other data, e.g. some url variable:

<txp:etc_query globals="_GET" query="2*{?factor|2|intval}" />

Now, if you append &factor=3 to the address bar string (feel free to test it), etc_query will retrieve and sanitize its value by casting it to integer, then multiply it by 2: 2×2=4. If factor is not set, it will be assigned a default value (2 here).

Any global variable (e.g. txp variable or thisarticle) can be retrieved in this manner, it suffices to modify the globals list. By default, globals seeks for variables in txp universe, like variable, pretext and so on. You can also assign the <txp:etc_query /> output to some <txp:variable /> by setting its name attribute. For example, to advance the value of <txp:variable name="some_var" /> by 1, call

<txp:etc_query name="some_var" query="{?some_var}+1" />

An alternative way

The calculations can also be done using etc_query as container:

<txp:etc_query globals="_GET" data="{?factor|2|intval}">
	2x{?}={2*{?}}
</txp:etc_query>

or

<txp:etc_query globals="_GET" data="{?factor|2|intval}">
	2x{?}={$*2}
</txp:etc_query>

both result in 2×2=4. The second form is internally different from the first one, and sometimes faster. Moreover, we can replace {$*2} therein by any PHP function, such as {$sqrt}: √2=1.4142135623731

So, what are these fancy {braces}?

They have special meaning inside etc_query, depending on the first symbol. As you have probably guessed, {?var} will retrieve the field var from the globals list of variables. We can optionally assign it a default value and pass it through some sanitizer functions, as above. A noticeable exception is {?}, which represents the value of the context data.

A {$func} pattern will call the PHP function func or some internal etc_query function, like in {$*2}. Functions can be passed arguments and chained, if necessary.

There are other kinds of special patterns, but the most important ones are plain {expressions}, like {2*2}. They will be evaluated by PHP DOMXPath functions, which leads us behind the scene.

What is behind the scene?

The core of etc_query is XPath language. It is used to evaluate query and other attributes. If you are acquainted with CSS or jQuery DOM traversal, there will be no difficulty in learning XPath.

First of all, XPath understands basic arithmetic, as well as some (limited) collection of functions, like count(...), substring(...) or starts-with(...). We can extend this collection with virtually any PHP function by setting functions attribute of etc_query:

<txp:etc_query functions="pow,sqrt" query="pow(sqrt(3),2)" /> = 3.0000000000001

But most importantly, XPath can be used for addressing parts of XML/HTML documents. The matched nodes can then be retrieved and transformed by etc_query, and that’s what it was made for.

The typical job for etc_query.

Sometimes, you are not quite happy with the output of hard-coded Textpattern tags. For example, we could benefit from HTML5 placeholder and required attributes in, say, the name input of comment_form. This can be done by replacing <txp:comment_name_input /> in comment_form with

<txp:etc_query data='<txp:comment_name_input />'
	query="input[@id='name']"
	replace="@@required=required@placeholder=Required" />

which tells etc_query to extract the input tag with id='name' from <txp:comment_name_input />, and replace its attributes as desired. You can see (and comment) the result in the comment form below.

Another typical example is the table of contents at the beginning of this page, which is obtained with

<txp:etc_query data="{?body}" query="//h4"
	break="li" wraptag="ol"
	label="Contents" labeltag="h4" html_id="contents">
	<a href="#{@id?}">{?}</a>
</txp:etc_query>

What else?

You can use etc_query to query databases, like this:

<txp:etc_query data="SELECT * FROM txp_users" wraptag="ul" break="li">
	{RealName?} was here on {last_access?}.
</txp:etc_query>

or to import RSS/XML/JSON feeds:

<txp:etc_query url="http://api.openweathermap.org/data/2.5/forecast/daily?q=Paris,fr&units=metric&cnt=5&appid=your_api_id"
	markup="json" query="list/*">
	<img src="http://openweathermap.org/img/w/{weather/0/icon?}.png" />
	{temp/day?}°C
</txp:etc_query>
Weather in Paris, FR
2024-12-06
moderate rain
9.84°C
2024-12-07
moderate rain
9.42°C
2024-12-08
moderate rain
6.61°C
2024-12-09
overcast clouds
5.94°C
2024-12-10
overcast clouds
4.03°C

… or even parts of webpages (hope they don’t mind):

<txp:etc_query
	url="https://xkcd.com/"
	query="id('comic')/img">
	<img{@src} loading="lazy" />
</txp:etc_query>
xkcd

or iterate over arrays/ranges (can you guess what are the double braces for?):

<txp:etc_query data="[1..9:2]" break="tr" wraptag="table">
	<txp:etc_query data="[1,2,3,5,7,9,11,13,17]" break="td">
		{{$*{?}}}
	</txp:etc_query>
</txp:etc_query>
1 2 3 5 7 9 11 13 17
3 6 9 15 21 27 33 39 51
5 10 15 25 35 45 55 65 85
7 14 21 35 49 63 77 91 119
9 18 27 45 63 81 99 117 153

or… but I feel that this article is getting too long. So, just download and install the plugin, read the included help, or get it on TXP Forum thread.

File(s)