Jump to content

Abstract Class Error


Nicole
 Share

Recommended Posts

Hello everyone,

I'm new to ProcessWire, and (this may sound cliché but) it's awesome... I've only used it for 3 days and already modified some of the admin UI for ease of use and configured a toolbar.

Today I was creating a module but I'm not really PHP-saavy, so when I got this error, I was pretty much stuck:

Fatal error: Cannot instantiate abstract class Inputfield in C:\xampp\htdocs\processwire\wire\core\Fieldtype.php on line 79

This module uses the integer inputfield and the page inputfield, I noticed that on Fieldtype.php, line 78 says: "// TODO make this abstract" so I'm wondering if that could have something to do with the fact that it's not working or if there's something wrong with my code.

Thanks in advance.

//Nicole

  • Like 1
Link to comment
Share on other sites

Hello there and welcome to the forum! Always nice to see new faces around :)

About that error, could you perhaps post some sample code about exactly how and where you're using these inputfields in your module? That would be very helpful in solving this. (I've used integer inputfield in module before and haven't had any problems, so I'm guessing it might have something to do with the way it's used.)

Edit: it would also be helpful to know what kind of module we're talking about here -- is it a Process module or..?

Edited by teppo
Link to comment
Share on other sites

Hello there and welcome to the forum! Always nice to see new faces around :)

About that error, could you perhaps post some sample code about exactly how and where you're using these inputfields in your module? That would be very helpful in solving this. (I've used integer inputfield in module before and haven't had any problems, so I'm guessing it might have something to do with the way it's used.)

Edit: it would also be helpful to know what kind of module we're talking about here -- is it a Process module or..?

Thanks for your help...

It's a fieldtype module and might take longer to build than I thought... I want to retrieve the integer for "number of pages to display" and the page inputfield for "parent page of the displayed pages" then take those values and output the markup on the page. I wanted to make it a "markup" module but couldn't quite get how to insert it into a specific page. It's basically a "blog" or "page list" module. This is my code:

<?php

/**
*
* Copyright 2013 by Nicole Bardales
*
*
* ProcessWire 2.x
* Copyright (C) 2012 by Ryan Cramer
* Licensed under GNU/GPL v2, see LICENSE.TXT
*
* http://processwire.com
*
*/

class FieldtypeBlog extends FieldType {

public static function getModuleInfo() {
return array(
'title' => 'Blog',
'summary' => 'Displays a Page List',
'version' => 1,
'author' => 'Nicole Bardales',
'href' => '',
'permission' => 'page-edit',
);
}

public function init() {
}

public function getConfigInputfields(array $data) {
$inputfields = new InputfieldWrapper();

//ask for number of pages to display
$f = $this->modules->get("InputfieldInteger");
$f->attr('name', 'numberofpages');
$f->label = 'Number of Pages to Display';
if(isset($data['numberofpages'])) $f->value = $data['numberofpages'];
$inputfields->append($f);

//parent page for children to be displayed
$f = $this->modules->get("InputfieldPage");
$f->attr('name', 'parentpage');
$f->label = 'Display pages located beneath';
if(isset($data['parentpage'])) $f->value = $data['parentpage'];
$inputfields->append($f);

return $inputfields;
}

public function sanitizeValue(Page $page, Field $field, $value) {
}

public function BlogPageList($event) {
$class = "page-list";
$limit = $data['numberofpages'];
$page = $data['parentpage'];
$results = $this->$pages->find("parent=$page, limit=$limit, sort=-date");
$event->return = "<ul class='$class'>";
foreach($results as $result) {
echo "<li><h3><a href='{$result->url}'>{$result->title}</a></h3><p>{$result->summary}</p></li>";
}
$event->return = "</ul>";
}
}

Thanks again :)

Link to comment
Share on other sites

I think the problem lies here:

public function getConfigInputfields(array $data) {
$inputfields = new InputfieldWrapper();
//ask for number of pages to display
$f = $this->modules->get("InputfieldInteger");
$f->attr('name', 'numberofpages');
$f->label = 'Number of Pages to Display';
if(isset($data['numberofpages'])) $f->value = $data['numberofpages'];
$inputfields->append($f);
//parent page for children to be displayed
$f = $this->modules->get("InputfieldPage");
$f->attr('name', 'parentpage');
$f->label = 'Display pages located beneath';
if(isset($data['parentpage'])) $f->value = $data['parentpage'];
$inputfields->append($f);
return $inputfields;
}

because if I simply use this code:

public function getInputfield(Page $page, Field $field) {
 $inputfield = $this->modules->get('InputfieldPage');
 return $inputfield;
}

or

public function getInputfield(Page $page, Field $field) {
 $inputfield = $this->modules->get('InputfieldInteger');
 return $inputfield;
}

everything seems to work fine, although I still can't get it to output anything but that must be an error, either in the echo on the template or on the field's output.

Link to comment
Share on other sites

Hi Nicole & welcome to the PW forums,

I've not written any FieldType extensions yet but does making this change help?...

public function ___getConfigInputfields(Field $field) {
 $inputfields = parent::___getConfigInputfields($field);

 ... // rest of this method's code as per your original getConfigInputfields() code.

  • Like 1
Link to comment
Share on other sites

Hi Nicole, welcome to ProcessWire :)

I'm going to take a step back here and ask why you want this as a markup module in the first place?

This could more easily be done by simply having a form in the relevant template file and doing things that way instead. I think a module is over-complicating something that is more easily achieved another way.

For example:

<form action="<?php echo $page->url; ?>" method="post">
 <label>Number of pages</label>
 <input type="text" name="totalPages" /><br />
 <label>Category</label>
 <select name="parentPage">
   <option>Select</option>
   <?php
   foreach ($pages->get("name=blog_categories")->children as $parentPages) { // Alter to select whatever the parent is that the categories are stored under
       echo "<option value='$parentPages'>$parentPages->title</option>";
   }
   ?>
 </select><br />
 <input type="submit" value="Submit" />
</form>
<?php
if ($input->post->count() && (int)$input->post->totalPages > 0 && $input->post->parentPage > 0) { // Did we post any values?
   foreach ($pages->find("parent={$input->post->parentPage}, limit={$input->post->totalPages}") as $result) {
       echo $result->title . "<br />"; // <-- Your formatting here
   }
}
?>

I have assumed that the selectable page parents are all on the same level and would simply be categories for the blog and that you don't want someone to be able to select from ALL pages in the site, but you can alter the list to suit pretty easily.

Link to comment
Share on other sites

Welcome Nicole.

I had a look at your module and there's quite some things missing. I'm with Pete that for what you want to create there isn't the need for a new fieldtype. But I'm not sure what exactly you're trying to achieve that you have come up with this approach, what is the goal?

Just to explain a little about fieldtypes/inputfields. A fieldtype always needs an inputfield associated. The inputfield is then used as the "interface" to input values. The inputfields all have a render() method that will output markup in the editpage screen at the end. Also there's a database schema you should specify and also wakeupValue, sleepValue, sanitizeValue, formatValue and getBlankValue. So you can guess it's much more sophisticated and complex than you initially thought and you're mixing a lot of thing up that makes it hard to start with. Other "normal" WireData modules that hook into something or process modules that can create custom admin pages functionality are much simpler to start with.

Also the getConfigInputfields() is to have settings for your module and not for something you went here. You could use it to globally define the two values of course, but it will only be available to superusers to edit it.

Solutions

To get back to what you want to acheive. I think Pete is right, but I think you don't want to create a front-end form?

A good solution to have this on a page to configure, simply use two fields to have a integer (or range slider) and a page field to select the parent page. Basicly what you wanted to do with your module. Then you can use them in the template code to output the list. You can attach those two field to whatever template/page you want, maybe the home page or some other pseudo "settings" page you create.

A code in the template then maybe looks like this: "parent_page" being the page select, "limit" being the integer field


$settings = $pages->get("/settings/");
$results = $this->$pages->find("parent=$settings->parent_page, limit=$settings->limit, sort=-date");
echo "<ul class='$class'>";
foreach($results as $result) {
echo "<li><h3><a href='{$result->url}'>{$result->title}</a></h3><p>{$result->summary}</p></li>";
}
echo "</ul>";

For such a small thing it isn't really needed to have a module that renders that but you could create a helper function to include in your templates, or if you really want to start with modules, I think the following might be simpler to start with. Look at HelloWorld.module aslo to learn more.

This is a module you can load and use its methods to call in templates.

<?php

class MyBlog extends WireData implements Module{

   public static function getModuleInfo() {
       return array(
       'title' => 'Blog',
       'summary' => 'Displays a Page List',
       'version' => 1,
       'author' => 'Nicole Bardales',
       'href' => '',
       'singular' => true,
       'autoload' => false
       );
   }

   public function init() {
       // attach hooks maybe, not needed here
   }

   public function blogPageList($limit = 10, $parent = null) {
       $class = "page-list";
       /* or you could retrieve the setting from a fixed page */
       //$limit = $this->pages->get("/settings/")->limit; 
       //$parent = $this->pages->get("/settings/")->parent_page;
       if(!$parent) return "Specify a parent int he second argument";

       $results = $this->$pages->find("parent=$parent, limit=$limit, sort=-date");

       if(!count($results)) return "No pages found";
       $out = "<ul class='$class'>";
       foreach($results as $result) {
           $out .= "<li><h3><a href='{$result->url}'>{$result->title}</a></h3><p>{$result->summary}</p></li>";
       }
       return $out . "</ul>";
   }
}

With this you can load the module in your templates and use the method it adds to output the list.

// load module, since we made it non autoload
$blog = $modules->get("MyBlog");
$parent = $pages->get("/blogposts/");
echo $blog->blogPageList(10, $parent);

or

// load module, since we made it non autoload
$blog = $modules->get("MyBlog");
$parent = $pages->get("/settings/")->parent_page;
$limit = $pages->get("/settings/")->limit;
echo $blog->blogPageList($limit, $parent);
Edited by Soma
mixed up module code
  • Like 6
Link to comment
Share on other sites

@Soma - I think you meant "achieve" not "archive" throughout that post. I wouldn't normally correct someone (especially since my language skills other than English are pretty poor) but it could cause confusion in this case.

  • Like 1
Link to comment
Share on other sites

Thanks Pete, I'm happy if someone corrects me if it's vital. :)

I just wanted to add another possiblity.

You can also use the /site/config.php to add configuration you need throughout your site.

Add this to the config file.

$config->blogPageListLimit = 10;
$config->blogPageListParentId = 1004;

And you can use it wherever you need it.

$pages->get($config->blogPageListParentId)->children("limit=$config->blogPageListLimit, sort=-date");
  • Like 1
Link to comment
Share on other sites

@Soma: I've got nothing to add or otherwise contribute here, but just wanted to say what an impressive explanation you've come up with! Good job as always - this time it just felt like a simple 'like' isn't enough. :)

  • Like 2
Link to comment
Share on other sites

@Pete, hi Pete, thanks for helping. I wanted to create the module since some friends want to be able to add page-lists at will, simply by defining the parent-page and the limit of pages. Maybe it's because they're used to another CMS. And as Soma mentioned later, it's not meant to be a front-end form, the inputfields simply get the details then output them where I need them in the markup (or they're supposed to, anyway).

@Soma, thanks for helping as well. After it didn't work I imagined it would be harder than I thought, maybe it's better to put this project on hold until I grasp PW's basics. Your solution could work well, although I was aiming to create back-end interface where users simply select the parent page, the limit and the code is outputted where needed, for ease of use (no code involved). But I think I'll show them PW is different and how they can make it even more awesome. Plus as @nik and @netcarver said, what a great explanation Soma, thanks for taking the time to deal with me.

Thanks to all...

  • Like 1
Link to comment
Share on other sites

From your description, I have another question :)

Why not simply use two separate fields instead of combining them into one fieldtype? Am I guessing correctly that this module was supposed to give them the choice of selecting specific options when adding a page in the admin interface then?

Link to comment
Share on other sites

OK, I might get that to work, is there a way to output the markup without making them edit code? For example, there's a field called "Number of Pages" that allows them to write an integer, and another one called "Parent Page" that allows them to select the page that will be taken as parent for the page list. Is there something like an existing fieldtype that can be added, takes the data from the previous fields and then outputs the markup?, I just noticed the "Markup" inputfield, could I use that?

PS, let me know if I didn't explain it well, it makes sense in my head but I'm not sure it does here :P

Wait, I think I might have something here....

Link to comment
Share on other sites

Well the markup would be outputted to the template file on the front-end wouldn't it? Or am I missing something again and this is supposed to be totally back-end for some reason?

If the resulting page content is front-end, then edit the front-end template file and you can reference them as $page->parent_page and $page->number_of_pages or whatever you've named them.

I guess I'm still not sure where the output is supposed to be in this case but if they fill out those fields in the admin then in the template file you would have something like:

foreach ($pages->find("parent={$page->parent_page}, limit={$page->number_of_pages}") as $result) {
   echo $result->title . "<br />"; // <-- Your formatting here
} 

That's similar to some code from earlier. They don't need to edit that code as you would be the only one editing the templates.

Of course I could still be miles off from what you're trying to do :D

Link to comment
Share on other sites

Nicole, that's what my first solution is like, and this is how it's meant to work. You add the two fields to the page you want them and in the template file you output markup that will take those two values as to construct the selector needed. If you set that up, nobody has to change code, but you can edit it via those 2 fields.

In PW you don't get fields that output markup by itself, you need at least some echo $page->field.

Of course it would be possible to make the field output a markup string and not the plain or formated value. But it goes beyond what you really need and it would take a long post to explain everything. Doing a fieldtype which has two inputfields is even more complicated and in the end it is what the template/field setup in PW is made for, so you'd only mimic what is already there. :)

My solutions show already what you could do otherwise, you have some alternatives.

Link to comment
Share on other sites

@Pete and @Soma thanks for bearing with me. I realize that's similar to something you shared before Soma, an idea struck me but I guess I'm not getting anywhere with it. Yes Pete, you got it right, the code is outputted on the front-end and it does look like that, and yes Soma, they don't need to deal with the code since I'm the one outputting it. I guess I made PW difficult even though it's easy :P

-- I just remembered something I read on PW: "Forget all that clutter you're used to dealing with in other CMSs"... :D

  • Like 1
Link to comment
Share on other sites

Well the rule of thumb is that if you're doing anything consisting of simple manipulation of tree (like getting X children of a parent Y), it should not be more than ten lines, otherwise you're solving the problem the wrong way.

And yes, there are multiple things people usually go at the wrong way with PW. and yes, I just pulled the number ten out of my… anyway.

Note: and by wrong way, I mean "doing it the hard way you'd do it in other CMS"

  • Like 1
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...