TypeRocket / core

TypeRocket core source files where all the magic lives.
https://typerocket.com
36 stars 21 forks source link

Has Many Repeater #31

Closed eric-michel closed 5 years ago

eric-michel commented 5 years ago

Hey @kevindees, here's my proposed HasManyRepeater field.

One note: I know you mentioned that you're slow to add fields, so it still needs a decent amount of cleaning up and further testing. I didn't want to do all the work of folding it more seamlessly into core until I knew it's something you were interested in merging into master.

All that said, here's an example for you to test how it works!

We're gonna do a one-to-many relationship with Graduating Classes having many Students:

CREATE TABLE `wp_students` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `class_id` bigint(20) unsigned NOT NULL,
 `first_name` varchar(50) NOT NULL,
 `last_name` varchar(50) NOT NULL,
 `gpa` decimal(2,1) NOT NULL,
 `sort` int(11) NOT NULL DEFAULT '0',
 PRIMARY KEY (`id`),
 KEY `Class` (`class_id`),
 CONSTRAINT `Class` FOREIGN KEY (`class_id`) REFERENCES `wp_posts` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

And its corresponding model in typerocket/app/Models:

namespace App\Models;

use \TypeRocket\Models\Model;

class Student extends Model {
    protected $resource = 'students';
}

I have my forked version of TR with the HasManyRepeater branch installed inside a theme. In my functions.php I've got the following:

require( 'typerocket/init.php' );

add_action( 'typerocket_loaded', function () {
    $classes = tr_post_type( 'Graduating Class', 'Graduating Classes' );

    $classes->setTitleForm( function () {
        $form = tr_form();

        $first_name = $form->text( 'first_name' )->setLabel( 'First Name' );
        $last_name  = $form->text( 'last_name' )->setLabel( 'Last Name' );
        $gpa        = $form->text( 'gpa' )->setType( 'number' )->setLabel( 'GPA' )->setAttribute( 'step', '0.1' );

        $students   = new \TypeRocket\Elements\Fields\HasManyRepeater( 'Student', 'class_id', $form );
        $students->setFields( [
            $first_name,
            $last_name,
            $gpa
        ] );
        $students->setLabel( 'Students' );

        echo $students;
    } );
} );

This should generate a Repeater that works almost exactly the same as your existing field, except that each record you add, update, or delete will correspond to a record in the Students table.

Also, deleting a record doesn't remove it from the DOM like the normal Repeater, but rather highlights it in red and adds a form value that marks it for deletion when the form is updated (and this can be undone prior to form submission).

I've also added some options.

By default, sorting is disabled. You can choose to either automatically order by any field within the related table $students->orderBy( 'first_name' ) or reactivate manual sorting by feeding the HasManyRepeater an integer field in the table dedicated to containing the sort order $students->sortable( 'sort' ).

In addition, I've replicated every relevant Model query method so that you can filter what gets fed into the repeater. This includes where, orWhere, take, and orderBy.

Plenty more to talk about in terms of ideas to improve the field, but I've gone on long enough. Let me know your first impressions!

eric-michel commented 5 years ago

Hey @kevindees, sorry to pester you, but curious if you've had a chance to look at this to see what you think. I'm going to be doing another pass to clean up a lot of code and add a couple features, but don't know yet if I should do so in the version I have that's outside of core, or the one that alters things within core.

If this isn't something you're interested in folding into core I totally understand; just hoping to find out soonish so I know where to do my refinements.

kevindees commented 5 years ago

Hey @eric-michel

I'm not sure if I will add this to the core but I have thought about having a list of user-made fields posted on the website and in the docs. This field would work really well in that concept. I'm going to look into how best to do that so anyone can make custom fields and have them installed via composer.

I just need to add a class that would register the field into the form system using macros.

What PHP/JS hooks would you need to make this field work?

Thanks, Kevin

eric-michel commented 5 years ago

@kevindees great question. If I'm not mistaken, you may already have the hooks necessary to bring in the JS and CSS (or they could just be enqueued from the plugin).

The most invasive changes I had to make were to the WPPostController class update and create methods (in order to save data to the related table) and the Model class getFieldValue method (to appropriately retrieve the data for the view from the related table).

Ideally I'd be able to hook into those methods and do my thing. Right now I'm just extending the classes, but of course that could break functionality for any other fields that someone might try to install that also have to extend those classes.

kevindees commented 5 years ago

@eric-michel

I'm going to table this request for core integration. I would be cool to see it as an options custom field that can be added via composer.