2

Is it technically possible to get the next ID before saving a dataobject? The ID of the new, until now not created record.

The only solution I came up with, was creating a new table, where I'll save the latest ID, every time a new dataobject of type 'x' was created and than count up.

csy_dot_io
  • 1,199
  • 6
  • 19

2 Answers2

3

The ID of the new, until now not created record.

Even if you did get the "ID" as @schellmax suggested you'd never be sure that the same "ID" can be used when saving. This if quite fragile and can be broken if e.g. multiple users try to do the same thing approximately at the same time.

Here's what I did for a project that needed to upload and attach files to a record before submitting the form.

  1. Save the record in the beginning regardless of wether it'll be submitted or not. Now you have the official ID for that record.
  2. Attach any files / other DataObjects as you would when you already have the record.
  3. When submitting the form optionally clean up the table in a way that doesn't destroy other in-use records.

This way you have the correct ID no matter what happens and how many users try this at the same time. Also the performance hit should be minimal unless you have hundreds or thousands of users doing this at concurrently.


Example (not sure it works as-is as I stripped it down):

public function QuoteForm() {
    $fields = new FieldList();

    // Clears out quotes older than 7 days
    Quote::removeEmptyQuotes();

    // Get the quote.
    $quote = $this->getQuote();

    $yourName = new TextField(
        'Name',
        _t('QuoteForm.YourName', 'Your name')
    );
    $yourName->setAttribute('required', true);
    $fields->push($yourName);

    $uploadField = new UploadField(
        'Files',
        _t('QuoteForm.Files', 'Select file*'),
        $quote->Files()
    );
    $uploadField->setRecord($quote);
    $uploadField->setFolderName('quotefiles');
    $uploadField->setConfig('canAttachExisting', false);
    $uploadField->getValidator()->setAllowedExtensions(array(
        'jpg', 'jpeg', 'png', 'gif', 'tiff',
        'odt', 'pdf', 'rtf', 'txt',
        'doc', 'docx',
        'ppt', 'pptx',
        'xls', 'xlsx'
    ));
    $uploadField->setAttribute('required', true);
    $fields->push($uploadField);

    $actions = new FieldList(
        new FormAction('saveQuoteRequest', _t('QuoteForm.Send', 'Send'))
    );

    return new Form($this, 'QuoteForm', $fields, $actions);
}

/*
 * Selects quote based on possible QuoteID from request.
 * If none are found / if the request param is empty,
 * creates a new Quote.
 *
 * @return Quote
 */
protected function getQuote() {
    $quote = null;
    $quoteID = (int)Session::get('QuoteID');
    if ($quoteID > 0) {
        $quote = Quote::get()->byID($quoteID);
    }
    if ($quote === null) {
        $quote = new Quote();
        $quote->write();
    }
    Session::set('QuoteID', $quote->ID);
    return $quote;
}
jlilja
  • 400
  • 1
  • 5
  • 17
  • How do you save the record in the beginning? Have the user to do this by hitting the save button or do you do this automatically? – csy_dot_io Jan 26 '15 at 15:18
  • Automatically by calling `$dataobject->write();`. That way we have the `ID` before giving the user the form. Then the form (and possibly session) can just keep a reference to that `ID`. – jlilja Jan 27 '15 at 06:47
  • Where and when do you call this? – csy_dot_io Jan 27 '15 at 11:38
  • When you first create the dataobject: `$dataobject = new DataObject(); $dataobject->write();`. The same place you'd retrieve the `ID` manually. – jlilja Jan 28 '15 at 07:02
  • but that would be in onBeforeWrite? Sry but I don't get it. There's no init function on a Dataobject is it? Until now I can't figure out where to place this, so that the user get's a written object after he hits the create button. Could you please post an example? – csy_dot_io Jan 28 '15 at 07:28
  • Example added to the answer – jlilja Jan 28 '15 at 07:38
  • Ah. Now I understand what you mean. I should have said, that the user is creating the record in the backend over a gridfield. So I think that wouldn't work in my case. But generally that would be a solution. – csy_dot_io Jan 28 '15 at 08:54
  • I think you could hook to the creation of the record as well. At least by extending the create new button... – jlilja Jan 28 '15 at 09:05
  • @csy_dot_io if you are adding stuff that requires you to know the id then onBeforeWrite wont work until you have a id. So id suggest you add a notice to your dataobject editor that states "save first to be able to add x " and then show the fields on the dataobject form. Would that kind of flow work for you better? Or just use onAfterWrite(). Suggesting as an alternative for the accepted answer – Olli Tyynelä Jan 28 '15 at 10:45
  • @FinBoWa that would be a solution but the user should be able to edit everything from the beginning. I'll try to extend the create new button like koodimyyra mentioned – csy_dot_io Jan 28 '15 at 13:30
1

You could query the database first:

$nextId = MyDataobject::get()->sort('ID')->last()->ID + 1;
schellmax
  • 5,678
  • 4
  • 37
  • 48
  • you're totally right, my fault. i also had a look at http://stackoverflow.com/questions/6761403/how-to-get-the-next-auto-increment-id-in-mysql, but @koodimyyra definitly has the right answer here. – schellmax Jan 26 '15 at 08:47