How to create a GraphQL schema with PHP and webonyx/graphql-php

Hello everyone!

Many nowadays developers are using different ways of communication between front end (client web browser) and back end (server).
One of these ways is GraphQL schema language. Using GraphQL, a developer can easily transfer data between the front end and the back end. Also you can quickly change rules or create a new one to modify structure of data. In GraphQL the rules are called – Schema. Schema consists of: Objects, Fields, Data types, etc. Also there are three types of communication with the server: Query, Mutation, Subscribe. These types describes schema of fields and types and each schema provides its principle to transfer data.

Along with this GraphQL has a different implementation in different programming languages with the same abilities. In other words, GraphQL is a schema language that is implemented in a programming language as a service. Each programming language, for instance: PHP, JavaScript, Java, Python, Ruby, etc., has a side-developed library to work with GraphQL schema. It isn’t builtin a module/library and it isn’t a part of any programming language.

That being said, GraphQL has many different implementations and below I am describing an implementation for PHP language and the library is called: webonyx/graphql-php by creating a simple Query schema. It is a quick way to create simple schema and use it to receive data from the server and then use it in your client scripts.

Being written on PHP language this library can be installed into your scripts with the Composer package manager. As described on the GitHub page of the library, you can just run the next command:

	composer require webonyx/graphql-php

Also, to parse input requests to the scripts you need to install the laminas-diactoros library by the next command:

	composer require laminas/laminas-diactoros

After finishing these commands the library will be able to use it in your project by including the file autoload.php in your script like this:

	require_once('vendor/autoload.php');

To begin using the libary and creating a simple Query scheme, it is required to define the classes of the library that will be used to process the Query Scheme:

use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Server\StandardServer;
use GraphQL\Upload\UploadMiddleware;
use Laminas\Diactoros\ServerRequestFactory;
use GraphQL\Server\ServerConfig;

Recently I ran into a problem implementing a filter for flats for one of my clients, on his real estate website, and below I will try to describe a simple way to quickly create an elementary schema for a filter like that. So, after defining the classes used for creating a schema, let’s start describing a simple object for the flats Query:

1. Creating an Object for Query schema:

$flats_item = new ObjectType([
	'name' => 'FlatItem',
	'fields' => [
				'id' => Type::int(),
                                'floor' => Type::int(),
				'num' => Type::int(),
				'price' => Type::int(),
				'area_total' => Type::int(),
				'complex' => Type::string()
	]
]);

$flats_items = new ObjectType([
	'name' => 'FlatsItems',
	'fields' => [
			'items' => Type::listof($flats_item)
	]
]);

$flats = [
	'type' => $flats_items,
	'args' => [
			'price_min' => Type::int(),
			'price_max' => Type::int(),
	],
 'resolve' => function($rootValue, $args) {
			// some code to make query answer
 }
];

The code above describes the structure of flats. Each flat has a few parameters such as id, floor (floor number), num (flat number), price, etc. The next variables($flats_items) contains a list of the flat items. Finally in the $flats variable the type of the query is defined, which is assigned by $flats_items variable and arguments that the schema can receive in the resolve function.

After defining the flats schema, you need to add into Schema Query:

$query = new ObjectType([
  'name' => 'Query',
  'fields' => [
    'flats'           => $flats,
  ]
]);

$schema = new Schema([
    'query' => $query,
]);

The assinging of the flats schema to the Schema Query is above, which means you need to create a new ObjectType in $query variable and then assign this variable to the Schema object.
Having done these several actions let’s move on to writing a code for using the Schema defined above.

2. Writing the code for processing the Query Schema

	$server_config = ServerConfig::create()
	->setSchema($schema);

	$request = ServerRequestFactory::fromGlobals();

	$rawInput = file_get_contents('php://input');

	$input = json_decode($rawInput, true);

	$request = $request->withParsedBody($input);

	$server = new StandardServer($server_config);
	$result = $server->executePsrRequest($request);
	$server->getHelper()->sendResponse($result);

In the lines above you need to create an instance of ServerConfig class and assign the Query Schema. Then, you will need to create a request instance using the class ServerRequestFactory from the laminas-diactoros library. After that, you need to pass the data from the input to the request, the data sent from the client to the server. Finally,
you will need to create a server instance, which will be using the ServerConfig instance, and a request instance to output the request’s response using method – sendResponse.

At the same time the response to your request may contain errors. But not all errors will appear in the response, only the errors about the Query Schema will. Other errors don’t add to the response. You need to do additional actions to get these errors, so if your purpose is to debug your code you need to add these lines after the server response code:

$debug = DebugFlag::INCLUDE_DEBUG_MESSAGE | DebugFlag::INCLUDE_TRACE;
$output = $result->toArray($debug);
file_put_contents('/path/to/file',json_encode($output));

This code is converting all debug data to an array and saving it into the file in the JSON format.

3. Preparing the response data in the resolve function

In the code above, in the schema definition, there is a resolve function as an associated element of the array. Now to test our script that provides the response with the data of the flats on the filter request, you need to define the test data inside this function. The input arguments for the schema were price_min and price_max; these arguments also are called the fields in the request that you can send to the server. For example, let’s define a four flats in an array with the fields corresponding to our defined schema.

<?php

$flats = [
  [
          'id' => 1,
          'floor' => 2,
          'num' => 3,
          'price' => 100,
          'area_total' => 10,
          'complex' => 'Complex 1'
  ],
  [
          'id' => 2,
          'floor' => 2,
          'num' => 3,
          'price' => 200,
          'area_total' => 10,
          'complex' => 'Complex 2'
  ],
  [
          'id' => 3,
          'floor' => 2,
          'num' => 3,
          'price' => 300,
          'area_total' => 10,
          'complex' => 'Complex 3'
  ],
  [
          'id' => 3,
          'floor' => 2,
          'num' => 3,
          'price' => 400,
          'area_total' => 10,
          'complex' => 'Complex 1'
  ],
];

All in all, by writing the array of data for the flats, you need to write a simple function of filtering this data based on filter arguments – price_min or price_max. This code can be written like this:

return ['items' => array_filter($flats,function($flat) use ($args) {

				if($flat['price']>$args['price_min'])
						return true;
				else
						return false;

})];

The code uses the anonymous function in the context of PHP builtin function array_filter. The anonymous function gets each element of the flats array and the request data in the $args variable. Inside the function there is a check that is comparing the price field of each element with the filter argument – price_min and then if the price is greater than the filter argument, the flat passes to the finall array of the items for the response.

To illustrate, the query may be like this:

query flats {
  flats(price_min: 200) {
    items {
      id
      floor
      price
    }
  }
}

and the response will be like this:

{
  "data": {
    "flats": {
      "items": [
        {
          "id": 3,
          "floor": 2,
          "price": 300
        },
        {
          "id": 3,
          "floor": 2,
          "price": 400
        }
      ]
    }
  }
}

It means when you pass the variable/field in the request – price_min with the value – 200, you will receive the response only with two flats of the four because they are correspond to the filter argument and the others don’t.

Also, in the request I use only three fields – id, floor and price, but in the Query Schema there are a few more. You can easily add them into the query and receive new information in the response. For example:

query flats {
  flats(price_min: 200) {
    items {
      id
      floor
      price
      complex
    }
  }
}

After that, all flats will have a new field – complex with the information from the server that you have defined earlier.

{
  "data": {
    "flats": {
      "items": [
        {
          "id": 3,
          "floor": 2,
          "price": 300,
          "complex": "Complex 3"
        },
        {
          "id": 3,
          "floor": 2,
          "price": 400,
          "complex": "Complex 1"
        }
      ]
    }
  }
}

The code of this script you can get on the Gist.

Full review about GraphQL type system you can read on this page.


The Details tag for creating disclosure widget

Almost all web developers run into the problem of implementation of a disclosure widget. In the HTML5 standard implementation that is supported by all modern browsers there is a special HTML tag that allows you to create a disclosure widget. The tag is:

<details>

Using this tag you can create disclosure widget without writing any code on JavaScript. Below is an HTML markup:

          <details>
            <summary>Widget title</summary>
            <p>Widget content</p>
          </details>
        

The result of this code is rendered below:


Widget title

Widget content


On clicking the label – the Widget title or the black triangle, the widget expands and collapses. When the widget is expanded the widget content becomes visible. On clicking the label again, the content becomes hidden. It means you can toggle the state of the widget.

The detail tag has a special attribute of its state called – open. When the widget is open the details tag is having the attribute open which can have a boolean value – true or empty value.

Otherwise when the widget is closed the open attribute can have a boolean value – false or the attribute may be removed.

But on the object level the attribute – open always exists and has a boolean value. In a markup the attribute may be ommited.

This simple widget will work in every modern browser excluding Internet Explorer and Edge.

Also the widget appearance can be changed by CSS styles. For example the black triangle can be replaced with plus and minus signs depending on the state of the widget. The widget title can be decorated with a grey gradient background. CSS styles are below:

details.style1
{
 margin-bottom: 10px;
}

details.style1 > summary::-webkit-details-marker
{
 display:none;
}

details.style1 > summary
{
 position: relative;
 cursor: pointer;
 background: linear-gradient(#dbdbdb,#898989);
 display: inline-block;
 padding: 5px;
 padding-left: 25px;
 border-radius: 5px;
 outline: none;
 width: 100%;
 text-align: center;
}

details.style1 > summary:hover
{
 background: linear-gradient(#bbbbbb,#737373);

}

details.style1 > summary::after
{
 display: block;
 position:absolute;
 content: '+';
 font-size: 20px;
 font-weight: bold;
 top: 0px;
 left: 5px;
}

details.style1[open] > summary::after
{
 content: '-';
}


Widget title

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.

Widget title 2

Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.


First widget has the open attribute and the widget is rendered in the expanded state. All these widgets have been created totally without JavaScript.

Nevertheless sometimes you need to check the state of the widget and do some actions depending on its state. For example when a user attempts to change the state of the widget by clicking on it, the others widgets must collapse. It can be implemented only with JavaScript, the code is below:

window.onload = function() {

  var details = document.getElementsByClassName('collapsed');
  for (var i = 0; i < details.length; i++) {
    var element = details[i];
    element.addEventListener("toggle", function(event) {
      if (this.open) {
        for (let i = 0; i < details.length; i++) {
          let element = details[i];
          if (element !== this) {
            element.open = false;
          }
        }
      }
    });
  }
}

The JavaScript code above begins working when a page is fully loaded. Then all the detail tags with collapsed class are chosen and we start the loop with these elements. Inside the loop for each element we set up a function that will be called whenever the specified event is fired. In our case the event will be fired when a user is clicking on the widget title and the event has name toggle. In the function we are checking the current object with each object from the details variable inside a loop too. If current object isn’t equal to the object from the details variable then we set the open attribute of this object to a boolean value – false.

Thus the all the widgets that exclude a clicked widget will be collapsed and the clicked widget will be expanded. The examples are below:



In conclusion, I think it is a fast and a simple way to create disclosure widgets without coding and loading third party libraries like jQuery, etc. But you should keep in mind that this solution isn’t working in Internet Explorer and Edge.


Error: MySQL said: Access denied for user ‘root’@’localhost’ in MySQL 5.7

Hello everyone!

If you are trying to install new MySQL instance and want to connect to this instance through SSH tunnel with GUI software like Sequel Pro or MySQL Workbench then you can’t help meeting with the error: MySQL said: Access denied for user ‘root’@’localhost’ . Especially if you are installing a Percona MySQL Server.

It occurs because MySQL resolves address localhost as 127.0.0.1 and by default in mysql.user table exits a record for localhost users only. But in case if you create a new user for 127.0.0.1 and try to connect through SSH you can get the same error. You can solve these errors with simple action, just put this single line in your my.cnf in [mysqld] section:

[mysqld]
skip-name-resolve = 1

How to add an extra parameters in Visual Composer

Hello everyone

If you need to add an extra parameter to standard visual composer elements, you can do it in a few steps:

1. First, you need to attache your own function to a visual composer hook your own function, the visual composer hook is – vc_after_init. You need to write a code like this:

add_action( 'vc_after_init', 'vc_after_init_actions' );

Where vc_after_init_actions() must be your function name.

2. Secondly you need to create your function where you will set new parameters for standard elements:

function vc_after_init_actions() { 

    $new_args = array(
      array(
       'type' => 'dropdown',
       'heading' => __( 'Column type', 'js_composer' ),
       'param_name' => 'column_type',
       'value' => array(
         'Content column' => 'content_column',
         'Sidebar_column' => 'sidebar_column',
       ),
       'description' => '',
     )
   );
   vc_add_params('vc_column',$new_args);
}

In the code above I have used standard field settings which you can use in vc_map() function and I added this settings array to standard element settings with vc_add_params() function. After that you add these line codes to your project, you can see your defined field in the settings of a standard element in the admin panel.

Also you can remove fields from element settings with function vc_remove_param(). For example:

 vc_remove_param( 'vc_row', 'full_width' );

Jade nested mixins

Hello everyone!

If you are a frontend developer and you are using gulp/grunt/webpack with the template engine like Jade,  it is very a likely that you face with a problem like Рhow to create mixin with mixin inside.

In other words, you need to set mixin with your random name and display it inside your parent mixin in the particular place. It seems like a javascript object with methods which would be called in loop by their names.

You can do it in Jade with the code like this:

//- global object for nested mixins

- blocks = {}

//- mixin for set other mixin into global object for nested mixins

mixin set(key)

- blocks[key] = this.block

mixin container-for-content

.container

//- It needs for declaring place where nested mixis will displayed.

block

.blocks
  //- Runs loop for iterates each block in global blocks object and runs "render" function
  each element, i in blocks
  = blocks[i]()

Now you can use this construction like this:

+container-for-content
 +set('first-block')
   .first-row
     | First row
 +set('second-block')
    .second-row
     | Second row

And as a result you should get output like this:

<div class="first-row">First row</div>
<div class="second-row">Second row</div>

MODx how to create a xtype combobox based on modResource

Hello everyone!

In this post I will try to explain how to create a xtype combobox for your custom CMP in MODx.
This can be helpful if you create a CMP page using this tutorial – Developing an Extra in MODX Revolution.

Let’s get started!

Part 1: create Javascript/Extjs template

In my case I need to create a combobox in an update window and I need to load to this combobox a modResource entity by some criteria. For example for these entities I need to set parent id in my query in xPDOObject.

First you need create combobox xtype/template and will name it – modx-combo-employees:

 MODx.combo.Template = function(config) {
     config = config || {};
     Ext.applyIf(config,{
         name: 'manager_id'
         ,hiddenName: 'manager_id'
         ,displayField: 'pagetitle'
         ,valueField: 'id'
         ,pageSize: 20
         ,fields: ['id','pagetitle']
         ,tpl: new Ext.XTemplate('<tpl for="."><div class="x-combo-list-item"><span style="font-weight: bold">{pagetitle}</span>'
             ,'<tpl if="pagetitle"> - <span style="font-style:italic">{pagetitle}</span></tpl>'
             ,'<br />{description}</div></tpl>')
	 ,url: testimonials.config.connectorUrl
         ,baseParams: {
             action: 'mgr/managers/getList'
         }
         ,allowBlank: true
     });
     MODx.combo.Template.superclass.constructor.call(this,config);
 };
 Ext.extend(MODx.combo.Template,MODx.combo.ComboBox);
 Ext.reg('modx-combo-employees',MODx.combo.Template);

In this code example I use xtype/template from modx-combo-category but I have some changes.

According to the official tutorial you must have a separate directory with the name like – doodles.
In my case I created directory with the name – testimonials.
And entities/objects that are displayed on CMP page named – Testimonials.
Each testimonial has some parameters like name/text/phone/photos/avatar/etc.
And it has a parameter – manager_id.
The id of this parameter belongs to modResource object type which contain standard fields like – name/pagetitle/content/etc.
And I need to receive these fields in my combobox to display a name of managers in it.

For receive this data to in combobox you need to create processor for this combobox, path to this processor defined in JS code above:

 baseParams: {
  action: 'mgr/managers/getList'
 }

Part 2: create php processor

You need to create the processor for connector – getlist.class.php in directory – path_to_your_package/mgr/managers and then defined your class.
In my case like this:

class ManagersGetListProcessor extends modObjectGetListProcessor {
		public $classKey = 'modResource';
		public $defaultSortField = 'id';
		public function prepareQueryBeforeCount(xPDOQuery $c) {
				$c->where(array(
				    'parent' => 1683
				));
				return $c;
		}

Public method prepareQueryBeforeCount provides a possibility to change query in xPDOObject before it executes and returns the results to the connector.
I change WHERE clause and set parent id because I have collection of my entities with this id. But you can set any other criterias for xPDOObject like – join/sort/limit and etc.
This collection contains managers that I need to displayed in my combobox.

In conclusion

After completing the above these actions you should get the combobox like this:
screen-shot-2017-08-28-at-20-12-31


How to set limit of input file field with jQuery

Hello everyone!

By default in current implement of HTML in modern browsers input tag doesn’t support limit for uploaded files.
And if you need to set limit of maximum upload files in your field:

<input type="file" name="photos" multiple />

you can use simple jQuery snippet:

$(document).ready(function(){
	var limit_of_files = 5;
	$('input[type="file"]').on('change',function(){

		if($(this).prop('files').length>limit_of_files)
		{
			alert('You exceeded limit for uploading files! Maximum of uploading files is 5!');
			$(this).val('');
			return false;
		}

	});
});

How to delete zero byte files in your folders

It is easy peasy like this:

find . -type f -name "*.php" -size 0 -print0 | xargs -0 rm

And that’s all!


New order email is not sending for new clients in woocommerce

Hi there

If you met with problem when your clients don’t receive the letter about a new order after they complete the checkout form and you are using any plugin for modify the checkout form fields, and your checkout form doesn’t contain the field with name – billing_email, then you need to add this code to your functions.php:

function custom_billing_email_field($id, $recipient, $object)
{
	if(empty($object->billing_email))
	{
		return $_POST['your_custom_field_name'];
	}
	return $object->billing_email;
}

add_filter('woocommerce_email_recipient_customer_on_hold_order','custom_billing_email_field',10,3);

Where variable $_POST must contain your custom email field –

$_POST['your_custom_field_name']

.
And you need to use filter for your default type order status for new orders, in my case it is a hold status and filter –

woocommerce_email_recipient_customer_on_hold_order

.

But other filters also exists, they are formed from the concatenation strings – woocommerce_email_recipient_ and id of email WC_Email class in get_recipient() method:

	/**
	 * Get valid recipients.
	 * @return string
	 */
	public function get_recipient() {
		$recipient  = apply_filters( 'woocommerce_email_recipient_' . $this->id, $this->recipient, $this->object );
		$recipients = array_map( 'trim', explode( ',', $recipient ) );
		$recipients = array_filter( $recipients, 'is_email' );
		return implode( ', ', $recipients );
	}


Disable woocommerce product gallery metabox

It is very simply remove this metabox with follow code:

add_action( 'add_meta_boxes' , 'remove_my_meta_boxes', 40 );
function remove_my_meta_boxes()
{
    remove_meta_box( 'woocommerce-product-images',  'product', 'side');
}