In this tutorial, we will make use of VueJS and PHP to build a really
cool keyword density tool. On the Vue side, we’ll make use of
components, templates, and root elements, and more. We will use PHP to
generate the form we need, as well as to handle form processing, in
addition to making use of the substr_count() function to provide the data we need. Let’s see how we can do this, it should be fun.
You’ll notice that the tr tag also makes use of Vue’s custom markup. What it is doing there is dynamically populating each row of the table with two td elements. One for the search Term, and one for the Number of times, or Count of how often that search term appeared in the haystack. Behind the scenes, we are actually making use of substr_count() to find this data for use, then we use Vue to display the results in a beautiful interactive table with sorting capabilities.
Declare The Vue Component
The first step that we will take is to declare a component for our application. In this instance, we are creating akeywordcounter
component as shown by the first argument passed to the .component()
method. We pass an object as the second parameter to this method. It has properties of template
, props
, data
, and methods
.
The template is a string which serves as the actual markup for the Vue
component. In this example, it begins with a hashtag, which means that
Vue will treat this string as a querySelector for which that innerHTML
will be used as the template string. Our props property is an object,
which in this case contains to properties, data and columns. The values
of these properties are arrays, and we will fill them dynamically a
little later on. The data property holds a function, or rather
references a function. There is a bit of magic happening here, as Vue
actually turns any properties into getters and setters which is what
gives the framework it’s reactive nature. Finally, we have the methods
property, which is an object that contains methods to be used in the Vue
instance. It is a means of keeping all of your functionality neat and
tidy. Methods in the methods property have their this context set to the Vue instance.
<script>
Vue.component('keywordcounter', {
template: '#keywordcounter-template',
props: {
data: Array,
columns: Array
},
data: function () {
var sortOrders = {};
this.columns.forEach(function (key) {
sortOrders[key] = 1
});
return {
sortKey: '',
sortOrders: sortOrders
}
},
methods: {
sortBy: function (key) {
this.sortKey = key;
this.sortOrders[key] = this.sortOrders[key] * -1;
}
}
});
</script>
Present a form to enter a haystack and keyword(s) to search
In this step we will use a simple combination of HTML and PHP to present a form for which we can collect some data. First off, we have a form element with the action set to substr_count.php and the method is post. What this means is that we will use the post http method to submit any data we collect to the PHP file named substr_count.php. In our case, this is literally a single page application, so all snippets on this page can be assembled in order to create your own working example. Inside of the form tag, we have a textarea which will accept the data to be known as haystack, and a text input which will collect a comma separated list of keywords that we will analyze in the haystack data. Our inputs make use of the isset PHP function in order to repopulate the inputs on each successive testing of data. This way, you wouldn’t have to re enter all of the information into the form in order to search on say a different set of keywords. Finally we have just a simple button which allows us to submit the form.
<form action="substr_count.php" method="post">
<label for="haystack">Enter text to analyze here.</label>
<textarea name="haystack" id="haystack" class="form-control" rows="7"><?php if (isset($_POST['haystack'])) {
echo $_POST['haystack'];
} ?></textarea>
<label for="keyword">Enter comma separated keywords to search for here.</label>
<input name="keyword" id="keyword" type="text" class="form-control" value="<?php if (isset($_POST['keyword'])) {
echo $_POST['keyword'];
} ?>">
<button type="submit">Submit</button>
</form>
Define the keywordcounter-template template
In the section above where we talked about the string template, and how it is used as a querySelector when prefixed with a hashtag. Below, we now find ourselves making use of<script type="x-template">
to define our template. This is how the component finds it’s template.
This section here contains the markup and logic that will define the
data we present to the user. In our case we have a simple table with
some bootstrap styling. You’ll notice that the th and tr tags hold some
special markup. This is the markup of VueJS which is able to turn our
plain HTML into markup capable of logic processing. In the th tag, we
use v-for to loop over any available data, give it a nice style of a
hand pointer, attach a click handler which fires the sortBy() method,
apply an active class if the current table is active, capitalize the
first letter, and determine if the sort is ascending or descending. In
short, this markup allows us to click on the table header to sort by
that column. When we run our little application, we will be able to
sort by the Term, or by the Count of each term.You’ll notice that the tr tag also makes use of Vue’s custom markup. What it is doing there is dynamically populating each row of the table with two td elements. One for the search Term, and one for the Number of times, or Count of how often that search term appeared in the haystack. Behind the scenes, we are actually making use of substr_count() to find this data for use, then we use Vue to display the results in a beautiful interactive table with sorting capabilities.
<script type="text/x-template" id="keywordcounter-template">
<table class="table table-hover">
<thead>
<tr>
<th v-for="key in columns"
style="cursor:pointer;"
@click="sortBy(key)"
:class="{active: sortKey == key}">
{{key | capitalize}}
<span class="arrow"
:class="sortOrders[key] > 0 ? 'asc' : 'dsc'">
</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="
entry in data
| orderBy sortKey sortOrders[sortKey]">
<td v-for="key in columns">
{{entry[key]}}
</td>
</tr>
</tbody>
</table>
</script>
Define the root element
This markup corresponds to the name we gave to the component we registered in the first step. Now we can reference it with these custom HTML tags like so.
<div id="findkeywords">
<keywordcounter
:data="keywordcounterData"
:columns="keywordcounterColumns">
</keywordcounter>
</div>
Fill the data that populates the component
In this step, we actually have to use PHP to generate portions of the script in our pages, since this is the data that will be dynamic based on the information that we fill out in the form and submit to the application. We actually have two cases here, if the data submitted results in several terms and counts, the first branch is taken. If only one term and count is found, we take the second branch.
<?php
if (isset($_POST['haystack']) and isset($_POST['keyword'])) {
$haystack = $_POST['haystack'];
$keyword = $_POST['keyword'];
if (strstr($keyword, ',')) {
$i = 1;
$keywords = explode(',', $keyword);
?>
<script>
// fill the data that populates the component
var action = new Vue({
el: '#findkeywords',
data: {
searchQuery: '',
keywordcounterColumns: ['term', 'count'],
keywordcounterData: [
<?php
foreach ($keywords as $keyword) {
echo ' { term: "' . $keyword . '", count: ' . substr_count($haystack, $keyword) . ' },';
}
?>
]
}
});
</script>
<?php
} else {
?>
<table class="table table-hover">
<thead>
<tr>
<th style="cursor:hand;"> Term <span class="arrow asc"> </span></th>
<th style="cursor:hand;" class="active"> Count <span class="arrow asc"> </span></th>
</tr>
</thead>
<tbody>
<tr>
<td><?php echo $keyword ?></td>
<td><?php echo substr_count($haystack, $keyword); ?></td>
</tr>
</tbody>
</table>
<?php
}
}
?>