Allow empty or existing value not in list when submit editor form

Allow empty or existing value not in list when submit editor form

asleasle Posts: 114Questions: 31Answers: 0
edited June 22 in Editor

I have a <select> list in records from the main db publikasjon with values from a field isbn.litt_isbn. I pick the first available (=0) value from the DB field as an option and after submitting it is marked used (=1). This works. But I want to be able to accept existing field value and also no(empty) value. I have problems getting theValidate::: working and I get an error when submitting

<b>Fatal error</b>
:  Uncaught Error: Call to a member function optional() on string in /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php:174
Stack trace:
#0 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php(1032): DataTables\Editor\Validate::_common('\xEF\xBB\xBF978-82-345-0...', 'isbn')
#1 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Field.php(778): DataTables\Editor\Validate::DataTables\Editor\{closure}('\xEF\xBB\xBF978-82-345-0...', Array, Object(DataTables\Editor\Field), Array)
#2 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(848): DataTables\Editor\Field-&gt;validate(Array, Object(DataTables\Editor), '4096')
#3 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(1071): DataTables\Editor-&gt;validate(Array, Array)
#4 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(716): DataTables\Editor-&gt;_process(Array)
#5 /home/1/m/mfu/www/code/js/Editor-PHP/controllers/upload-mfu.php(118): DataTables\Editor-&gt;process(Array)
#6 {main}
  thrown in <b>/home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php</b>
on line <b>174</b>
<br/>

Her is the field in js:

{
    label: 'ISBN:',
    name: 'publikasjon.litt_isbn',
    type: "select",
    placeholder: 'Ingen ISBN valgt',
    placeholderDisabled: false
},                   //other fields after this...

And the php code for the field:

Field::inst( 'publikasjon.litt_isbn' )
            ->options(function($db, $field) {
                $options = [];
                // Get the first available ISBN with status = 0
                $row = $db
                    ->query('select')
                    ->get(['isbn'])
                    ->table('isbn')
                    ->where('status', 0)
                    ->order('id ASC')
                    ->limit(1)
                    ->exec()
                    ->fetch();

                if ($row) {
                    $options[] = [
                        'label' => $row['isbn'],
                        'value' => $row['isbn']
                    ];
                }

                // Safely get the current value
                $current = null;
                if ($field !== null && method_exists($field, 'val')) {
                    $current = $field->val(); 
                }

                // If it's not the same as the one above, and it's not already in the list, add it
                if (!empty($current) && (!isset($row['isbn']) || $row['isbn'] !== $current)) {
            $options[] = [
                'label' => $current . ' (tidligere valgt)',
                'value' => $current
            ];
        }
                return $options;
                })
                ->validator(
                Validate::dbValues('isbn', 'isbn'),
                ValidateOptions::inst()->allowEmpty(true)
            )
        ,

Is this the correct way to do the sql to get the first field? I thin there is something I dont have correct in the Validate part but I can not find it out. So my goal is 1) Show current value if exists and accept it 2) Show first available number from isbn.isbn field. 3) Accept empty value on submit

Answers

  • asleasle Posts: 114Questions: 31Answers: 0
    edited June 22

    I think the error is with the Validate. It does not get the value to validate. When I run this

    ->validator(
            Validate::dbValues(),
            ValidateOptions::inst()->allowEmpty(true)
        )
    

    I get an error:
    Table for database value check is not defined for field publikasjon.litt_isbn
    But if I try this:

    ->validator(
            Validate::dbValues(),
            ValidateOptions::inst()
                ->table('isbn')
                ->field('isbn')
                ->allowEmpty(true)
        )
    

    I get this error:

    Fatal error: Uncaught Error: Call to undefined method DataTables\Editor\ValidateOptions::table() in /home/1/m/mfu/www/code/js/Editor-PHP/controllers/upload-mfu.php:78 Stack trace: #0 {main} thrown in /home/1/m/mfu/www/code/js/Editor-PHP/controllers/upload-mfu.php on line 78

  • allanallan Posts: 64,623Questions: 1Answers: 10,684 Site admin

    The ValidateOptions class doesn't have table or field methods. See the API reference docs for the class here.

                ->validator(
                    Validate::dbValues('isbn', 'isbn'),
                    ValidateOptions::inst()->allowEmpty(true)
                )
    

    from your first post looks more correct to me. You need to specify the table / column information for the validator since it can't derive it automatically from the options function (since it is a custom options function - which looks fine).

    What happens if you use that?

    Allan

  • asleasle Posts: 114Questions: 31Answers: 0
    edited June 23

    I tried your suggestion now and when I try to add a value or no value and update I get this:

    <b>Fatal error</b>
    :  Uncaught Error: Call to a member function optional() on string in /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php:174
    Stack trace:
    #0 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php(1032): DataTables\Editor\Validate::_common('\xEF\xBB\xBF978-82-345-0...', 'isbn')
    #1 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Field.php(778): DataTables\Editor\Validate::DataTables\Editor\{closure}('\xEF\xBB\xBF978-82-345-0...', Array, Object(DataTables\Editor\Field), Array)
    #2 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(848): DataTables\Editor\Field-&gt;validate(Array, Object(DataTables\Editor), '4096')
    #3 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(1071): DataTables\Editor-&gt;validate(Array, Array)
    #4 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(716): DataTables\Editor-&gt;_process(Array)
    #5 /home/1/m/mfu/www/code/js/Editor-PHP/controllers/upload-mfu.php(116): DataTables\Editor-&gt;process(Array)
    #6 {main}
      thrown in <b>/home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php</b>
    on line <b>174</b>
    <br/>
    

    The value I am sending to the table publikasjon while the select value is coming from table isbn . Is there some logic I am missing? Also The existing value (if present) does not show. It shows always "Ingen ISBN valgt" if I leave the placeholder code in the field definition:

    {
        label: 'ISBN:',
        name: 'publikasjon.litt_isbn',
        type: "select",
        placeholder: 'Ingen ISBN valgt',
        placeholderDisabled: false
    },
    
  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429

    If you select the placeholder "No ISBN selected" (translated it from Swedish) you are sending exactly that to the server. That is not going to work because it is not an empty string. Try this please:

    {
        label: 'ISBN:',
        name: 'publikasjon.litt_isbn',
        type: "select",
        placeholder: 'Ingen ISBN valgt',
        placeholderDisabled: false,
        placeholderValue: "" //the placeholder value is empty
    },
    
  • asleasle Posts: 114Questions: 31Answers: 0

    Thanks, I still get the same error:
    <b>Fatal error</b>
    : Uncaught Error: Call to a member function optional() on string in /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php:174
    Stack trace:
    #0 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php(1032): DataTables\Editor\Validate::_common('', 'isbn')
    #1 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Field.php(778): DataTables\Editor\Validate::DataTables\Editor{closure}('', Array, Object(DataTables\Editor\Field), Array)
    #2 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(848): DataTables\Editor\Field->validate(Array, Object(DataTables\Editor), '4096')
    #3 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(1071): DataTables\Editor->validate(Array, Array)
    #4 /home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor.php(716): DataTables\Editor->_process(Array)
    #5 /home/1/m/mfu/www/code/js/Editor-PHP/controllers/upload-mfu.php(116): DataTables\Editor->process(Array)
    #6 {main}
    thrown in <b>/home/1/m/mfu/www/code/js/Editor-PHP/lib/Editor/Validate.php</b>
    on line <b>174</b>
    <br/>

    Also the current value is still not showing. If the record has a value the select still shows only 2 options, "Ingen ISBN valgt"(selected) and the first available value from isbn.isbn.

  • allanallan Posts: 64,623Questions: 1Answers: 10,684 Site admin

    Gah - I messed up the parameters for the dbValues method - sorry. Per the docs it is:

    dbValues( ValidateOptions $cfg=null, string $column=null, string $table=null, Database $db=null, array $valid=null)
    

    So you would use:

    ->validator(
      Validate::dbValues(
        ValidateOptions::inst()->allowEmpty(true),
        'isbn', // column
        'isbn' // table
      ),
    

    Allan

  • asleasle Posts: 114Questions: 31Answers: 0

    Thanks, this solved the update. I am able to update now (after adding the missing parenthes, no problem). The value, empty value or the value from "isbn.isbn" is added to the record but I am not able to read it back. The value shows in Datatables but in Editor it still is "Ingen ISBN valgt". The select does not show the current value. Any idea why? Here is my code now on the controller:

    Field::inst('publikasjon.litt_isbn')
            ->options(function ($db, $field) {
            $options = [];
            // Get the first available ISBN (status = 0)
            $row = $db
                ->query('select')
                ->get(['isbn'])
                ->table('isbn')
                ->where('status', 0)
                ->order('id ASC')
                ->limit(1)
                ->exec()
                ->fetch();
            if ($row) {
                $options[] = [
                    'label' => $row['isbn'],
                    'value' => $row['isbn']
                ];
            }
            // Get the current value in the record being edited
                $current = null;
                    if ($field !== null && method_exists($field, 'val')) {
                $current = $field->val();
                    }
                if (!empty($current) && (!isset($row['isbn']) || $row['isbn'] !== $current)) {
                    $options[] = [
                        'label' => $current . ' (tidligere valgt)',
                        'value' => $current
                    ];
                }
            return $options;
                 }) 
                ->validator(
                    Validate::dbValues(
                        ValidateOptions::inst()->allowEmpty(true),
                        'isbn', // column
                        'isbn' // table
                    )) 
    
  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429

    If you are using my and Allan's code suggestions everything should be fine - since you are not using a getFormatter which isn't required because ISBN is a simple string.

    Hard to tell without a test case, I guess. Could you provide one, please?

  • asleasle Posts: 114Questions: 31Answers: 0

    I am not sure what you mean but I made a test page here that you can create, edit and delete records.
    https://dev.mfu.no/code/admin-test.html
    If you look at the child data of a record you can find one with a valid ISBN number but it does not show in Editor. If you add the ISBN number suggested you will see it in the child row but not in Editor. The next time you edit or create a record you see a new ISBN number so it seems that logic is working.

  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429

    Hi asle,

    I used your test page and created two entries. Yes, there is only one ISBN to select but that is either ok or you have a problem with your options instance which looks a bit "weird" to me.

    I created one record that has the one and only ISBN and another one that doesn't have the ISBN filled. Looks fine in the data table and also in the respective Editor pop up.

    As I can see you are loading only this one ISBN option from the server:

    Roland

  • allanallan Posts: 64,623Questions: 1Answers: 10,684 Site admin

    It doesn't show in Editor because the options list only has a single option - which doesn't match the value of the ISBN in the row that was selected, hence it shows the placeholder.

    You have ->limit(1) in the options for getting the values for the ISBN. I don't quite understand what the intention is here - what's the point of having a select which only has a single option? Also, presumably you don't want to just list all ISBN's that would be insane. Is free text with an ISBN validator an option for you? As I say, I'm not clear on what the intention with this field is.

    One thing, it looks like you are expecting the options to update per row. That isn't the case for the way it is configured. If you need to load different options for select based on a row, you need to use dependent() and have it make an Ajax request to get the options to show for that row.

    Allan

  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429
    edited 10:07AM

    Something in addition to Allan's last remark. If you are requiring updated options per row you would need to load them when needed.

    That could be on "select" or on "preOpen" for example. You would pass the id to the server and retrieve the array of label / value pairs.

    editor
        .on('preOpen', function(e, mode, action) {  
            var id = 0;
            var that = this;
            if ( action === "edit" ) {
                var selected = yourDataTable.row( {selected: true} );
                if (selected.any()) {
                    id = selected.data().id;
                }
            }
            $.ajax({
                type: "POST",
                url: 'your url to get the Options',
                data: {
                    id: id            
                },
                dataType: "json",
                success: function (retrievedOptions) {   
                    that.field("yourSelectField").update(retrievedOptions);
                }
            });
        });
    
  • allanallan Posts: 64,623Questions: 1Answers: 10,684 Site admin

    Sorry - yes, use preOpen rather than dependent() for this. Good point @rf124!

    Allan

  • asleasle Posts: 114Questions: 31Answers: 0

    Sorry for the confusion. I will try to explain. I have a table ISBN that contains 1000 valid ISBN numbers. Every record (publication) should get a unique ISBN but not required when the record is created. When creating or editing a record my idea was to show the first free ISBN number. When the number is used it is marked "1" so the next record can choose the next free ISBN number (marked 0). This logic works now. The choosen number is added and marked "1". If the record has a ISBN number, display it. If not show "No valid ISBN". So I force to use the first free number in the ISBN list. Choosing "No valid ISBN" and updating should delete the current number and choosing the displayed number enters that in the record.

    Is using a <select> list a bad approach for this? As you see the ISBN is correct in the datatable so it would also be if I used a text field in Editor. I am a bit stuck since I thought the only problem was displaying the current value but I understand this is not so logical.

  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429
    edited 11:04AM

    It doesn't show in Editor because the options list only has a single option - which doesn't match the value of the ISBN in the row that was selected, hence it shows the placeholder.

    Since you have that limitation all you would need to do is to update the options on "preOpen". Just use my logic above and retrieve the relevant options which are:
    a) "create": The empty option plus the next available ISBN
    b) "edit": The empty option plus the next available ISBN OR the selected ISBN (if any)

    Don't know whether that is working with the placeholder and the placeholderValue. If not just modify my code like this for example:

    that.field("yourSelectField").update([{ label: "Ingen ISBN valgt", value: "" }]);
    that.field("yourSelectField").update(retrievedOptions, true);
    
  • rf1234rf1234 Posts: 3,122Questions: 91Answers: 429
    edited 11:36AM

    It could be something like this (provided that the placeholder and placeholderValue stuff still works). You would only need to call the server in case the record doesn't exist yet or no ISBN has been selected yet.

    editor
        .on('preOpen', function(e, mode, action) { 
            var that = this;
            if ( action === "edit" ) {
                var selected = yourDataTable.row( {selected: true} );
                if (selected.any()) {
                    var isbn = selected.data().isbn;
                    if ( isbn > "" ) {
                       that.field("yourSelectField").update([{ label: isbn, value: isbn }]);
                       return;
                    }
                }
            }
            $.ajax({
                type: "POST",
                url: 'your url to get the Options',
                dataType: "json",
                success: function (retrievedOptions) {  
                    that.field("yourSelectField").update(retrievedOptions);
                }
            });
        });
    

    This would retrieve the option in case it hasn't been saved yet (using Editor's db handler and Editor`s "raw" method):

    $stmt = ('SELECT isbn as label, isbn as value
                FROM isbn
               WHERE status = 0
            ORDER BY id ASC
                  LIMIT 1');  
    $result = $db ->raw()
                  ->exec($stmt);
    return $result->fetchAll(PDO::FETCH_ASSOC);
    
Sign In or Register to comment.