Jump to content

Database Counters Module


netcarver
 Share

Recommended Posts

This module provides a very simple interface to a set of named counters. You simply call a single function, next('name'), to pull the next value out of a counter - or to set it up if it does not yet exist. Next() takes a few extra parameters to allow you to increment by values other than 1 or to start at a certain number.

This provides some similar functionality to the built-in page naming feature of PW, and to this module recently posted by Stikki but I think it offers a little more flexibility than either. Having said that, I do like the simplicity of Stikki's new auto-increment module.

Module Availability

Here is my module on Github.

Here it is in the module repository.

Example Usage

Here's how this module can be used to title and name a new page by adding a couple of simple hooks to site/ready.php. This example applies to new pages using a template called 'invoice' that can be quick-added to the page tree. In order to get the following to work, you must edit the template that will be the parent of the 'invoice' template and setup the template for children to "invoice" and set the "Name Format for Children" field to something other than the default blank value (I use  title  as my value.)

<?php

/**
 * Function to recognise our special template.
 */
function isInvoiceTemplate($template) {
    return ($template == 'invoice');
}

/**
 * Pre-load the page title for invoice pages with a unique value
 * which includes a counter component.
 */
$pages->addHookBefore("Pages::setupNew", function($event) {
    $page = $event->arguments(0);

    $is_invoice = isInvoiceTemplate($page->template);
    $no_inv_num = $page->title == '';

    if ($is_invoice && $no_inv_num) {
        $counter_name = 'WR-' . date('Y');
        $number = $this->modules->get('DatabaseCounters')->next($counter_name, 10, 5000);
        $page->title = $counter_name . '-' . sprintf("%06u", $number);
    }
});

/**
 * Prevent ProcessPageEdit from forcing an edit of the name if we got here
 * through a quickAdd from ProcessPageAdd. We can do this because we
 * preset the title field in the Pages::setupNew hook.
 */
$pages->addHookAfter("ProcessPageEdit::loadPage", function($event) {
    $page = $event->return;

    $is_invoice = isInvoiceTemplate($page->template);
    $is_temp    = $page->hasStatus(Page::statusTemp);

    if ($is_invoice && $is_temp) {
        $page->removeStatus(Page::statusTemp);
        $event->return = $page;
    }
});

Note, the above code + module is one direct solution to the problem posted here by RyanJ.

post-465-0-56264100-1459552276_thumb.png

post-465-0-67628000-1459552450_thumb.png

Version History

1.0.0 The initial release.

  • Like 13
Link to comment
Share on other sites

To extend the above example to hide the name, template and parent fields on the settings page of invoices, you can add the following to the ready.php snippet in the above post.

/**
 * Work out if the template's settings should be tweaked to hide things that should be fixed...
 */
function shouldHideTemplateSettings() {

    // Super users retain super-setting-edit powers
    if (wire('user')->isSuperUser()) {
        return false;
    }

    // Are we even editing a page?
    if (wire('page')->process != 'ProcessPageEdit') {
        return false;
    }

    $id = (int) wire('input')->get('id');
    if (!$id) {
        return false;
    }

    $editedPage = wire('pages')->get($id);
    if ($editedPage->template->flags & Template::flagSystem) {
        return false;
    }

    if (!isInvoiceTemplate($editedPage->template->name)) {
        return false;
    }

    return true;
}

/**
 * Hide some fields from the invoice settings page from non-super-users.
 *
 * There's not much point allowing edits to the name field, so we want it immutable (at least) and probably totally
 * hidden (better.)
 *
 * We don't want invoices being moved from their parent, so we hide the parent field.
 * We don't allow them to use a different template, so we hide the template field.
 *
 */
if (shouldHideTemplateSettings()) {
    $pages->addHookAfter("ProcessPageEdit::buildFormSettings", function($event) {
        $wrapper = $event->return;

        // To show the name field but make it immutable
        $wrapper->_pw_page_name->collapsed = Inputfield::collapsedNoLocked;
        $wrapper->_pw_page_name->description = '';

        // Alternatively, to hide the name field do uncomment the following and comment out the 2 lines above
        //$wrapper->_pw_page_name->collapsed = Inputfield::collapsedHidden;

        // Hide the template changer, invoices can only use the invoice template
        $wrapper->template->collapsed = Inputfield::collapsedHidden;

        // Hide the parent page, as it is fixed in our page structure
        $wrapper->parent_id->collapsed = Inputfield::collapsedHidden;
    });
}
  • Like 6
Link to comment
Share on other sites

  • 2 weeks later...

Besides of my last question,

I followed the op's setup, but it didn't work at atll

post-2272-0-32536000-1460864209_thumb.pn

post-2272-0-39834500-1460864219_thumb.pn

This is my  /site/ready.php

instead of invoice, i changed it to order

<?php

function isOrderTemplate($template) {
    return ($template == 'order');
}

$pages->addHookBefore("Pages::setupNew", function($event) {
    $page = $event->arguments(0);
 
    $is_order = isOrderTemplate($page->template);
    $no_inv_num = $page->title == '';
 
    if ($is_order && $no_inv_num) {
        $counter_name = 'ORDER-' . date('Y');
        $number = $this->modules->get('DatabaseCounters')->next($counter_name, 10, 5000);
        $page->title = $counter_name . '-' . sprintf("%06u", $number);
    }
});

$pages->addHookAfter("ProcessPageEdit::loadPage", function($event) {
    $page = $event->return;
 
    $is_order = isOrderTemplate($page->template);
    $is_temp    = $page->hasStatus(Page::statusTemp);
 
    if ($is_order && $is_temp) {
        $page->removeStatus(Page::statusTemp);
        $event->return = $page;
    }
});

When I checked back the DatabaseCounter module config,

there is no such a counter named ORDER-   was created

Link to comment
Share on other sites

I'm curious, the

/site/ready.php

is not executed.

is there any other settings to activated it ?

None that I know of. Are you sure you have that file in the right place?

From second screen cap, how to make the invoice number (i.e.title field) not editable ?

Edit the template for your order pages in PW. Click on the title (invoice number) field and, on the input tab, set the visibility to whatever you need.

  • Like 2
Link to comment
Share on other sites

  • 2 months later...

Hi, I was trying this module becuase I this is the solution for my needs, I cant not make it work.

When I click new page I get the same as adrianmak , Title and name are not autopopulated.

 

I know my ready.php is working fine because I was trying this code from another post and the field was being autopopulated with the generated title:

$this->pages->addHookAfter('added', function($event) {
    $page = $event->arguments[0];
    if($page->template != 'your-template-name') return;
    $page->setAndSave('title', 'my page - ' . date("YmdHis"));
});

I'am missing something?


 
Link to comment
Share on other sites

  • 2 weeks later...
  • 1 year later...
4 hours ago, stuartsa said:

In the call to next(), what are 10 and 5000? And are the second and third arguments optional or required?

In the case of order numbers - for example - it is common not to start counting form 0 and not to increment by 1. This is a practice to make it harder for the customer to realize that a brand new webshop has just started selling... So as the comment says:

@param int $change (optional) The amount to change the counter by. Must be non-zero integer. Defaults to 1.
@param null|int $min (optional) If specified, the returned value will be greater or equal to this value.

"If specified, the returned value will be greater or equal to this value." actually means: initial starting value.

  • Like 1
Link to comment
Share on other sites

  • 5 months later...

Hi Netcarver and Ryan and All,

This is exactly what I'm looking for, I think.

I have a client who wants to add page content quickly, with just 3 input field (publish_date, body, tags) and not have to mess with the title or name fields, which we want to default to the page id. Now, one has to type in something like "n" (for new) in those fields, and then change them on the next screen by hand to the generated ID field.

I've programmed a user interface in a previous application (i.e. not using the admin pages), where I would fill in those fields with a temporary random number, and then replace them with the the actual page ids (I probably should have figured out how to grab the auto-increment number instead).

But doing an auto generate function like this for title and name should, in my opinion, be an option in PW, like a setting for a template that says:

"Check here if you want to bypass the title and name fields (on page creation), and have them filled in automatically with the auto-increment ID."

That would be GREAT.

Since that's not real yet, is the code above ready for production use, or is there something that someone else has done that will do this?

Thanks!

Peter

 

Link to comment
Share on other sites

@Peter Falkenberg Brown

Hello Peter,

I've not used this on a new site in a while (ie, not tested on a recent version of PW) but have no doubts the method works as presented above as I'm using on my own internal site to generate invoice numbers. The version of PW I am using there is 3.0.119.

If your general approach is the same, (given what you wrote above, I can't tell for sure) then I think this could work for you.

Best wishes,
Steve

  • Like 1
Link to comment
Share on other sites

@netcarver

Thanks, Steve, for your response. I'll dig into it!

@ryan and Ryan and Dev Team: I really do think that this would be a fabulous feature for ProcessWire:

- in every template, have a setting that could turn on a function to fill in the name and title fields with the auto-increment ID automatically, skipping that initial step where you see those two fields, and going right away to the main edit screen, where one would see the title value and the displayed url as the ID number.

In my work with PW, there have been numerous times when I wanted the name and title field to only be the ID, and it was a pain to have to program around that issue.

I think you'd get a LOT of positive feedback about this feature, especially if the setting was also in the API, i.e. add a record without having to specify the title and name field, but have them populated by the auto-increment ID.

Thanks!

Peter

 

Link to comment
Share on other sites

2 hours ago, Peter Falkenberg Brown said:

skipping that initial step where you see those two fields, and going right away to the main edit screen

There is already a feature similar to this - it's the "Name format for children" setting on the template of the parent page. https://processwire.com/docs/modules/guides/process-template/

ID is not one of the supported options, but if you want the page to be named with the ID you can install @kixe's module which extends the functionality of this feature.
http://modules.processwire.com/modules/process-setup-page-name/

Or you can enter something like a date format ("Y/m/d H:i:s") in the core "Name format for children" setting, which allows you to skip the Page Add step. And then set the page name and title to the ID with a hook in /site/ready.php:

$pages->addHookAfter('added', function(HookEvent $event) {
	$page = $event->arguments(0);
	if($page->template == 'your_child_page_template') {
		$page->setAndSave([
			'name' => $page->id,
			'title' => $page->id,
		]);
	}
});

 

Edited by Robin S
Updated hook for setting multiple page values
  • Like 3
Link to comment
Share on other sites

@Robin S @netcarver

Hi Robin and Steve,

Robin, the method you outlined above, with the ready.php code, worked perfectly! Thank you!

The module from @kixe generated errors, and after looking at a thread on it, I used your ready.php code instead.

Using the date/time method in the field could, I suppose, encounter a race conflict, but in this case, there's only one admin user.

@ryan

Ryan: I do think this deserves the addition of the id field in the code, and/or a switch option to do this exact function, with the auto-increment id, especially to avoid race conditions.

But... this works for me, today. Thanks again to both of you!

Peter

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...