Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.1k views
in Technique[技术] by (71.8m points)

postgresql 9.3 - Laravel 5.5 why is exception not thrown by foreign key violation from delete method?

Update 3: This problem from 9 months ago persists with Laravel 5.8 and Postgresql 11 on Windows 10 in a Laragon environment and Apache 2.4 on an Ubuntu machine. Does the Eloquent Model instance delete() method simply ignore foreign key constraints? It's baffling:

I have two tables in a Postgresql 9.3 database, companies and sites. There is a one-to-many relationship between them with companies on the one side. There is a foreign key constraint that prevents the deletion of a company if sites have been assigned to it. If I attempt to delete the company with the ID 'KSL' using an SQL query directly on the database, I get the expected error:

ERROR: update or delete on table "companies" violates foreign key constraint "sites_company_id_fkey" on table "sites" DETAIL: Key (company_id)=(KSL) is still referenced from table "sites".

I have defined an artisan command whose handler method has a simple try/catch block:

public function handle()
{
    $company = Company::find('KSL');
    try{
        $company->delete();
    }catch(PDOException $e){
        $this->info($e->getMessage());        
    }
}

When I run the command from the console, I get the expected error message:

SQLSTATE[23503]: Foreign key violation: 7 ERROR: update or delete on table "companies" violates foreign key constraint "sites_company_id_fkey" on table "sites" DETAIL: Key (company_id)=(KSL) is still referenced from table "sites". (SQL: delete from "companies" where "company_id" = KSL)

reflecting the native error message generated by Postgresql. However, when I call the delete method from a Controller using an Ajax call using a similar try/catch block, the exception is not caught and the call fails with no details of the error. I simplified the controller method to make it the same as the console handler:

    public function deleteModel(Request $request) {
        try {
            $id = 'KSL';
            $company = Company::find($id);
            $result = $company->delete();
            return 'success';
        } catch (PDOException $e) {
            return $e->getMessage();
        }
     }

Normally I would get the value of $id from the request argument. If I use a get request with a RESTful URL in the browser, I get a "The connection was reset" message in Firefox and a similar message in Chrome. I have referred back to an old question of mine which I thought had the solution, but running composer dump-autoload had no effect. I have cleared the cache, re-installed Laravel 5.5, updated my installation, and called composer dump-autoload again several times but the absence of any exception or logged error message gives me no clue. Debug is set to true for this development app.

I passed a handler to the PHP native function register_shutdown_function like this in the autoload.php file in the bootstrap folder:

register_shutdown_function(function () {
    $error = error_get_last();
    file_put_contents(__DIR__.'/../storage/crash.log', var_export($error, true));
});

Only the word 'NULL' appears in the crash.log. I checked the Apache 2.4 error.log file and the specific error log for this Laravel app but there no relevant details recorded there.

Here is the Exception Handler:

<?php

namespace AppExceptions;

use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Exception  $exception
     * @return IlluminateHttpResponse
     */
    public function render($request, Exception $exception)
    {
        return parent::render($request, $exception);
    }
}

UPDATE 1: I got a clue from this question: Stack size for Apache under Windows. I quickly tested my online applications (all running on Linux machines) and there is no problem. The exception is thrown correctly and a nice clear message is displayed for the user. My local environment is Windows and it looks like Apache suffers from this connection reset error more than in a Linux environment. I've increased the stack size in Apache as suggested by the answer to that question but it still doesn't work. I still get a connection reset error. I've re-installed Apache with the latest binaries from Apache lounge I'm running PHP 7.3. Can anyone shed some light on this?

UPDATE 2: An answer from Lucas to this question encouraged me to change server. When I ran php artisan serve from the console and then call the ParentTable->delete() method, I got the expected exception with no crashing. There is clearly something wrong with my Apache configuration. Unfortunately the accepted answer to that question doesn't solve my problem. I increased the stack size but the problem persists.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...