peanut's avatar

created() boot method trigger with validation?

I had it creating() to begin with, but these are associated tables and I need to attach the ID that is inserted after the post is created.. so, this is my code:

public static function boot()
{
    parent::boot();

    Post::created(function($post)
    {
        // Upload media
        if( Input::get('gallery_exist') == 1 )
        {
            foreach( Input::file('gallery') as $image )
            {
                $uploader = new Uploader();
                $uploader->setDirectory( static::$directory );
                if( ! $uploader->uploadImage( $image, array(static::$thumbnailWidth, static::$thumbnailHeight) ) )
                {
                    $post->errors()->add('upload', 'Image upload failed.');

                    // Remove post
                    $post->delete();
                    return false;
                }

                $file = $uploader->getRecentFile();
                // Insert image
                $media = new PostMedia();

                $media->project_post_id = $post->id;
                $media->url = $file;
                $media->type = 1;

                if( ! $media->save() )
                {
                    $post->errors()->add('gallery', $media->errors()->first() );

                    // Remove post
                    $post->delete();
                    return false;
                }
            }
        }

        if( Input::get('youtube_exist') == 1 )
        {
            foreach( Input::get('videos') as $video )
            {
                $media = new PostMedia();

                $media->project_post_id = $post->id;
                $media->url = $video;
                $media->type = 2;

                if( ! $media->save() )
                {
                    $post->errors()->add('video', $media->errors()->first() );

                    // Remove post
                    $post->delete();
                    return false;
                }
            }
        }

        // Upload product line attachments
        if(count( Input::get('product_line_id') ) > 0)
        {
            foreach( Input::get('product_line_id') as $product )
            {
                $line = new PostProduct();

                $line->project_post_id = $post->id;
                $line->product_line_id = $product;

                if( ! $line->save() )
                {
                    $post->errors()->add('product', $line->errors()->first() );

                    $post->delete();
                    return false;
                }
            }
        }

        return true;
    });
}

I'm passing $post->id in multiple places so the post model needs to save so created() works fine. But then if some of their image attachments fail, then I want to remove the post and return the user back to the screen with errors.. what's the best way of going about this? It doesn't seem like this is what I want since it ignores all validation rules for these scenarios and still publishes the post anyway and says everything was okay since it doesn't care about the return value like creating() does..

0 likes
4 replies
thepsion5's avatar

You could wrap the entire thing in a DB transaction and then, if validation fails at any point, rollback the transaction and then delete the created post.

1 like
peanut's avatar

I had that to get the ID.. so do I move all of this functionality into the save() method then? I wanted to use the model events that come with Laravel, but after it's been created, in the created() method, if it returns false, it still acts as if everything was okay and gives me a success message with no errors.

I had all of this working inside created().. so it would rollback on error. But Laravel won't recognize a return false in the created method.. so that's where my confusion lies.. how do I make it recognize that that returned false? Because it just ignores all the returns in there.. it only registers it when I change it to creating().. because once the model issues the save, it doesn't care.

thepsion5's avatar

I don't think Laravel can, since the created event is treated by Laravel as something that has already succeeded. By definition, an event that signifies "this object has been created successfully" can't be unsuccessful.

I think you'd be better off moving a lot of that code to a service class that handles building all of the secondary information that is part of the object without being an attribute of the model. That way, you can wrap the whole thing in a transaction and throw a validation exception should any part of it fail (which would automatically roll back the transaction in the process).

Something like:

public function createPost(array $postData, array $lineItemData, array $galleryData = array(), array $videoData = array())
{
    DB::beginTransaction();

    try {

        $post = new Post($postData);
        if( !$post->save() ) {
            throw new ValidationException($post->getErrors());
        }
        if( !empty($lineItemData) ) {
            $this->createLineItemData($post, $lineItemData);
        }
        if( !empty($galleryData) ) {
            $this->createGalleryData($post, $galleryData);
        }
        if( !empty($videoData) ) {
            $this->createVideoData($post, $videoData)
        }
    } catch(Exception $e) {
        DB::rollback();
        throw $e;
    }

    DB::commit();
}
1 like

Please or to participate in this conversation.