# Hooks for add-ons - Gridline 1.x documentation

> The PHP filters and actions around listing and saving, and the JavaScript API for adding buttons and behaviour to the grid.

Beyond columns, Gridline has hooks for code that changes how products are listed and saved, or adds to the Bulk Editor screen. Gridline Pro is built entirely on them.

## Checking a product before it's saved

`gridline_check` runs once a product's changes are applied and before it's saved. Return errors by column key; any error stops that product being saved, and the message appears on the cell.

```php
// Products in "Clearance" can't go above $50.
add_filter( 'gridline_check', function ( $errors, $product, $changed ) {
	if ( in_array( 'regular_price', $changed, true )
		&& has_term( 'clearance', 'product_cat', $product->get_id() )
		&& (float) $product->get_regular_price( 'edit' ) > 50 ) {
		$errors['regular_price'] = 'Clearance products cost $50 or less.';
	}
	return $errors;
}, 10, 3 );
```

## After a save

`gridline_save_result` filters what a save sends back to the grid. Its second argument lists every cell that changed as `[ product ID, column key, old value, new value ]`, which makes it a good place to log changes:

```php
add_filter( 'gridline_save_result', function ( $result, $changed ) {
	foreach ( $changed as list( $id, $key, $old, $new ) ) {
		error_log( sprintf( 'Bulk Editor: product %d %s %s → %s', $id, $key, wp_json_encode( $old ), wp_json_encode( $new ) ) );
	}
	return $result;
}, 10, 2 );
```

## Extra rows and request parameters

- `gridline_list_args` gets the arguments for a page of products and the REST request, so you can read your own (sanitized) query parameters.
- `gridline_product_rows` returns rows to show under a product, built with `Extendiment\Gridline\Editor::row()`. Pages still count products only.

Gridline Pro uses the pair for its "Show variations" option.

## The Bulk Editor screen

- `gridline_editor_load` fires when the screen loads. Enqueue your script there, with `gridline` as a dependency:

  ```php
  add_action( 'gridline_editor_load', function () {
  	add_action( 'admin_enqueue_scripts', function () {
  		wp_enqueue_script( 'my-grid', plugins_url( 'my-grid.js', __FILE__ ), array( 'gridline', 'wp-hooks' ), '1.0', true );
  	} );
  } );
  ```

- `gridline_page_actions` prints next to the screen's title; use `page-title-action` buttons there.
- `gridline_loaded` fires once Gridline has loaded (only when WooCommerce is active). Hook an add-on's setup to it; `GRIDLINE_VERSION` is defined from then on.

## The grid's JavaScript

The grid sets `window.gridline` and starts once the page is ready, after every script in the footer has run, so your script can add its `wp.hooks` first. Give your hooks your own namespace.

| Hook | Gets | Use it to |
|---|---|---|
| `gridline.init` (action) | the API | run once before the first load |
| `gridline.query` (filter) | query parameters, state | add parameters to the products request (read them with `gridline_list_args`) |
| `gridline.filters` (filter) | elements, state | add controls after the Filter button |
| `gridline.bulkActions` (filter) | elements, state | add buttons to the left of the filters |
| `gridline.selectable` (filter) | `false` | return `true` to show a checkbox column; selected row IDs are in `state.selected` |
| `gridline.rowClass` (filter) | class names, row | add classes to a row |
| `gridline.savedNotice` (filter) | HTML parts, result | add to the notice after a save (escape text with `gridline.escape()`) |
| `gridline.loaded` (action) | state | react after a page of products loads |

```js
// A button that shows how many products are on the page.
wp.hooks.addFilter( 'gridline.bulkActions', 'my-grid', ( nodes, state ) => nodes.concat(
	window.gridline.el( 'button', {
		type: 'button',
		class: 'button',
		text: 'Count',
		onclick: () => window.gridline.notice( 'info', window.gridline.escape( state.rows.length + ' products on this page' ) ),
	} )
) );
```

`window.gridline` also has `api( path, options )` for Gridline's REST routes, `setValue( row, key, value )` to make an unsaved change, `rowById()`, `render()`, `load()`, `go( changes )` (change filters or page and reload, after asking about unsaved changes), `pickImages()` for the media library, and more; the plugin's README lists them all.

## REST API

`gridline/v1/products` lists (GET, with the grid's parameters) and saves (POST, `changes` as `[ { id, key, value } ]`) for users who can edit products. Gridline Pro adds its routes to the same namespace.

Source: https://www.extendiment.com/plugins/gridline/docs/1.x/hooks-for-add-ons
