简体   繁体   中英

Laravel break foreach loop in Controller Method from Frontend

I have trouble interrupting a Controller method in Laravel Framework 4.2. I have a query which iterates through a table with ~100K records and checks several different attributes to redistribute them into several other tables.

I want to interrupt this loop on purpose, from front end. I tried a variable in the controller class and Session variable both to no avail.

I have also a progress bar which shows me the status of the loop. I have put a Button beneath it, to abort the method.

Controller:

class TeilnehmerController extends IXPController {

    public $abort; 

    public function recheckfailedbatch(){

        Session::forget('ALL');
        Session::forget('status');
        Session::forget('progress');
        Session::forget('abort');

        ini_set('memory_limit', '-1');
        ini_set('max_execution_time', '600');

        $anzahl['ALL']= anforderung_mangelhaft::all()->count();
        Session::set('ALL', $anzahl['ALL']);
        Session::set('status', true);

        $progress = "0";

        $all_fails = anforderung_mangelhaft::all();

        foreach ($all_fails as $fail){

          if ($this->abort){
              break(2);
          }

          $progress++;
          Session::set('progress', $progress);
          Session::save();

          // doing more secret fancy stuff here


       }

       Session::set('status', false);
       Session::forget('abort');
       Session::save();
       return Redirect::back()->with('successMessage', "Done.");

    }

    public function recheckfailedbatchstatus(){

        $status = Session::get('status');
        $progress = Session::get('progress');
        $all= Session::get('ALL');
          if ($all != 0){
            $percent = sprintf("%1\$.2f",$progress / $all * 100);

          }
          else{
            $percent = 0;
          }

        return Response::json(array('status' => $status, 'percent' => $percent));
    }

    public function recheck_abort(){

            #$this->abort = true;
            Session::set('abort', true);
            Session::save();
            return dump($this->abort);

    }

}

View:

@extends('layouts.default')

@section('subview')
    <div>
        <h2 class="mb20">mangelhafte Anforderungen</h2>
        <ul id="sectionsubnav">
            <li>
                <a href="{{action('TeilnehmerController@recheckfailedbatch')}}" ><i class="icon-refresh"></i>Erneute Pr&uuml;fung</a>
                <a href="#collapseSuche" data-toggle="collapse" aria-expanded="false" aria-controls="collapseSuche"><i class="icon-search"></i>Detailsuche</a>
            </li>
        </ul>

        @include('components.sysmessages')   

        @if(Session::get('status'))
        <div class="well well-lg">
          <h4>Pr&uuml;fung l&auml;uft</h4>
            <div class="progress">

              <div id="progress-bar" class="progress-bar progress-bar-striped active" role="progressbar"
  aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width:0%">
        </div>
      </div>
      <a href="{{ action('TeilnehmerController@recheck_abort') }}" onClick="return confirm('Diese Aktion kann nicht r&uuml;ckg&auml;ngig gemacht werden. Sind Sie sicher?');" id="abort" class="btn btn-danger btn-lg" role="button"><i class="icon-remove"></i>Abbrechen</a>

</div>
@endif
<!-- some more fancy content -->
@stop


@section('bodybottomscripts')
    @parent
    <script>
        $(document).ready(function() {

    var element = $("#progress-bar");
    var test = setInterval( function(){ bar_progress() }, 500);

    function bar_progress() {
            $.get( "mangelhaft/recheck-status", function( data ) {
            if (data['status'] == false){
                clearInterval(test);
            }             
            element.text(data['percent'] + '%');
            element.attr("aria-valuenow", data['percent']);
            element.width(data['percent'] + '%'); 
    });
    }
    </script>
@stop

Is it possible to do it in my intended way? I have also read about queues in laravel. Should i rather implement queues to do those kind of things?

the frontend has no idea about the backend and cannot influence a running script. This is because the front only gets the output from the back once its completed.

You need to rethink your approach and possibly use ajax to retrieve your data, then the clientside code can decide to either contact your API, or not, as the case may be.

First, I don't think that you can just use $this->abort to check for session. Replace it with Session::get('abort') and see if it works. Those two functions probably work in two different requests, and they have nothing related regarding class members or anything else on memory (with some exceptions, see further).

You may use a file to indicate abortion of running script. In recheck_abort , create an empty file (better to use temp directory of OS). Then instead of checking $this->abort , check for existence of that file. Do the same thing with this file as you did with session, unlink file if it exists at the very beginning of recheckfailedbatch and also unlink at the end.

Another solution is maybe to use shared memory . This is a bit more tricky, but gets the job done right.

I think the session gets read at start of request, and it does not reflect changes made with further request while current one is running. I have not checked Laravel source code to tell for sure, but it seems the session variables reside in memory while request is being processed.

Another workaround is to search on how to refresh sessions while request is being processed. That could mean checking Laravel source, but is it pretty neat to check.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM