Bernd: foreach unterbrechen bzw. erst später starten?

Hallo,

nur aus Interesse, da ich gerne die Ausgabe etwas umgestallten möchte. Stand jetzt

function StartEndePause($mysqli, $datum, $name) {
    
        $stmt = $mysqli->prepare("SELECT uss_id, uss_datum, uss_name, uss_wert, uss_inhalt
                                  FROM startendepause 
                                  WHERE uss_datum =? AND uss_name=?");
       
        $stmt->bind_param("ss", $datum, $name);
        $stmt->execute();
        $stmt->bind_result($uss_id, $uss_datum, $uss_name, $uss_wert, $uss_inhalt);
        $stmt->store_result();
        
        if($stmt->num_rows() >  0) {     
            while ($stmt->fetch()){
                
                $StartEndePause[] = array( 
                    
                    'uss_id'      => $uss_id, 
                    'uss_datum'   => $uss_datum,
                    'uss_name'    => $uss_name,
                    'uss_wert'    => $uss_wert,
                    'uss_inhalt'  => $uss_inhalt
                );
            }
            return $StartEndePause;
            }
    }

Die Ausgabe im HTML

$StartEndePause = StartEndePause($mysqli, $_GET['usz_datum_von'], $_GET['usz_name']);

<?php 
		if($StartEndePause > 0) {
		$i = 0;
		
		foreach($StartEndePause as $arraySEP){
			
			if ($arraySEP['uss_wert'] == "Pause") {
				$Pausegesamt += $arraySEP['uss_inhalt'];
				$i++;
			}	
		?>
			<?php if ($arraySEP['uss_wert'] == "Start") { ?>
				<span style="display: inline-block; width: 7.4em;">
					<?php echo htmlspecialchars($arraySEP['uss_wert']); ?>:
				</span> <?php echo htmlspecialchars($arraySEP['uss_inhalt']); ?> Uhr<br>
			<?php } elseif ($arraySEP['uss_wert'] == "Ende") {?>
				<span style="display: inline-block; width: 7.4em;">
					<?php echo htmlspecialchars($arraySEP['uss_wert']); ?>:
				</span> <?php echo htmlspecialchars($arraySEP['uss_inhalt']); ?> Uhr<br>
			<?php } else { ?>
				<span style="display: inline-block; width: 7.4em;">
					<?php echo htmlspecialchars($arraySEP['uss_wert']); ?>:
				</span> <?php echo htmlspecialchars($arraySEP['uss_inhalt']); ?> Stunde(n)<br>
		<?php
		}}?>
		<?php if ($i > 1): ?>
				<span style="display: inline-block; width: 7.4em; margin-top: 1em;">Pause gesamt:</span> 
				<?php echo htmlspecialchars($Pausegesamt); ?> Stunde(n)
		<?php endif ?>
		<?php }
		else { echo "Keine Daten vorhanden"; }
		?>

Die fertige Ausgabe schaut dann so aus

Kann ich mit leben, ist auch richtig. Jetzt frage ich mich allerdings ob man dieses etwas anderes gestallten könnte und zwar wie folgt:

  1. Start
  2. Pause gesamt
  3. Ende

Wenn ich jetzt auf Pause klicke sollen die Pausen einzeln zu sehen sein und die Gesamtpause sollte nach rechts verschoben werden und die Pause einzeln sollte umklammert werden mit einem }. Ich hoffe ihr versteht was ich meine.

Meine Frage ist also:

  1. Kann ich die Schleife unterbrechen und erst das komplett Ergebnis anzeigen?
  2. Kann ich nach Klick auf das Ergebnis alle Pause anzeigen lassen?
  1. Hallo Bernd,

    du stellst die falschen Fragen. "Unterbrechen" musst Du gar nichts. Nur zwei Varianten unterstützen. Aber dafür stellt sich die Frage nach dem gewünschten Weg.

    Wenn ich jetzt auf Pause klicke sollen die Pausen einzeln zu sehen sein

    Möchtest Du das durch einen erneuten Server-Request lösen oder rein client-seitig?

    Am Client gibt es die Möglichkeit, mit JavaScript die Pausendetails einzublenden. In dem Fall machst Du einen Button "Details" neben die Zusammenfassungszeile, reagierst auf den click des Buttons und blendest die Einzelzeilen ein.

    Es gibt auch den Checkbox-Hack: Das Wort "Pausen" ist das Label einer nicht sichtbaren Checkbox. Diese Box ist so platziert, dass ihr checked-Zustand die Detailsicht aktiviert. Aus PHP heraus musst Du dann natürlich die Zusammenfassung UND die Details liefern.

    Es gibt auch die neueren HTML Tags <details> und <summary>, die eine Aufklapplogik mitbringen, aber in Microsoft-Browsern (IE und Edge) nicht funktionieren. Dafür gibt es aber - sagt unser Wiki - Polyfills mit jQuery (und vermutlich auch ohne).

    Über einen Server-Request müsstest Du über einen URL-Parameter entscheiden, ob Du die Pausen zusammengefasst oder detailliert sehen willst, und dann die Ausgabe entsprechend gestalten. Der Umschalt-Button sollte dafür in einem eigenen kleinen Form mit method="GET" und einer entsprechenden URL liegen. Diese URL steuerst Du aus PHP so ein, dass bei geschlossenen Details die geöffnete Sicht angefordert wird und bei geöffneten Details die geschlossene Sicht.

    Was darf's denn sein?

    Rolf

    --
    sumpsi - posui - clusi
    1. Hallo,

      ich möchte es gerne ohne erneutes Senden lösen. Ich dachte, ich könnte es über CSS und jQuery lösen. Aber mir geht es erst einmal um das Zusammenfassen in der Schleife wenn es mehr als ein Eintrag für die Pause gibt.

      1. Hallo Bernd,

        offenbar ist dein arraySEP ja nach Start, Pausen und Ende sortiert.

        Ich würde - wie immer - zu EVA raten: Erst alles verarbeiten, dann ausgeben.

        	$i = 0;
          $pausen = [];
          $pauseGesamt = 0;
        	foreach ($StartEndePause as $arraySEP) {
        		$wert = $arraySEP['uss_wert'];
            
            switch ($wert) {
              case 'Start':
                $start = $arraySEP['uss_inhalt'];
                break;
              case 'Ende':
                $ende = $arraySEP['uss_inhalt'];
                break;
              case 'Pause':
                $pauseGesamt += $arraySEP['uss_inhalt'];
                $pausen = '{' . number_format($arraySEP['uss_inhalt'], 2, ',', '') . '}';
                break;
            }
          } ?>
          <span class="zeitZeile">Start:</span><?= htmlspecialchars($start) ?> Uhr<br>
          <span class="zeitZeile">Pause:</span><?php
            if (count($pauseGesamt) > 1) : ?>
               <span class="zeitDetail"><?= implode(' ', $pausen) ?></span>
            <?php endif ?>
            <?= number_format($pauseGesamt, 2, ',', '') . ' Stunde(n)' ?><br>
          <span class="zeitZeile">Ende:</span><?= htmlspecialchars($ende) ?> Uhr<br>
        
        

        Das hat unter anderem den Vorteil, dass die Ausgabereihenfolge nicht davon abhängt, wie die Daten in der DB stehen.

        Deine inline-Styles habe ich durch eine class ersetzt, das macht es lesbarer und sowas gehört eh ins Stylesheet. Ob dein Markup mit <span>blabla</span>wert<br> ideal ist, will ich auch bezweifeln, aber hier kein neues Thema aufmachen. Besser wäre z.B.:

        <label class="zeitRow"><span>Start:</span><output>17:30 Uhr</output></label>
        

        Und im stylesheet steht dann

        label.zeitRow { display: flex; }
        label.zeitRow > span:nth-child(1) { flex-basis: 7.4em; }
        

        Dadurch kommen <span> und <output> nebeneinander und jedes <label> auf eine eigene Zeile. Ich habe den span sehr genau selektiert, damit Du damit nicht den zeitDetails-Span triffst.

        Den span.zeitDetail musst Du nun mit geeigneten Mitteln ein- und ausblenden, dann geht der Rest von allein. Ein click-Eventhandler auf dem span, in dem das Wort "Pause" steht, ist nicht geeignet. Spans sind nicht interaktiv.

        Rolf

        --
        sumpsi - posui - clusi
        1. Hallo,

          danke für deine Hilfe und die Erklärung. Ich wollte die Pausen wie folgt ein und ausblenden

          $('#pause').click(function(){
            $('.zeitDetail').toggle();
          })
          

          Nichts tut sich, dann habe ich folgende Zeile gesehen und mir den Wert ausgeben lassen

          if (count($pauseGesamt) > 1)
          

          Damit sagst du ja, wenn die Pause größer als eine Stunde ist, richtig? Ich glaube ich habe mich falsch ausgedrückt, es soll aufklappen, wenn es mehr als eine Pause gibt, nicht wenn es mehr als eine Stunde zusammengerechnet gibt. Das heißt ich muss den Wert aus

          $pausen
          

          ermitteln?

          1. Hallo Bernd,

            sorry, count($pauseGesamt) ist Müll, das ist ja nicht das Array mit den Detaileinträgen.

            count($pausen) ist richtig.

            Rolf

            --
            sumpsi - posui - clusi
            1. Hallo,

              war auch mein erster Versuch. Wenn ich mir

              count($pausen)
              

              ausgeben lasse, erhalte ich eine 1. Was nicht richtig ist, wie du auf dem ersten Bild siehst sind es drei Einträge a 0,25. Also müsste da doch eine drei stehen?

              1. Hallo Bernd,

                ach Mist - ich bin heute schlecht drauf.

                $pausen soll doch ein Array sein, sonst wäre der implode sinnlos. Und das mach mit der Zuweisung im case "Pause" kaputt.

                Da hätte eine Warning kommen müssen - beim Entwickeln sollte man die nicht disablen:

                <b>Warning</b>: count(): Parameter must be an array or an object that implements Countable in <b>[...][...]</b> on line <b>2</b><br />

                In $pausen = '{' . number_format($arraySEP['uss_inhalt'], 2, ',', '') . '}'; fehlt ein [], es muss so aussehen:

                $pausen[] = '{' . number_format($arraySEP['uss_inhalt'], 2, ',', '') . '};

                Rolf

                --
                sumpsi - posui - clusi
                1. Hallo,

                  danke, jetzt klappt alles. Ich habe es wie folgt leicht abgeändert

                  label.zeitRow { display: flex; margin-bottom:6px; }
                  label.zeitRow > span:nth-child(1) { flex-basis: 3.4em; }
                  #zeitDetail {
                      margin-left: 55px;
                      padding-bottom: 10px;
                      display: none;
                      color: #999;
                  }
                  
                  .maus {
                  	cursor: pointer;
                  }
                  
                  <label class="zeitRow"><span>Start:</span><output><?= htmlspecialchars($start) ?> Uhr</output></label>
                  		<?php if ($pauseGesamt != 0): ?>
                  		<label class="zeitRow"><span id="pause" <?php if (count($pausen) > 1) : ?> class="maus" <?php endif ?>>Pause:</span><output><?= number_format($pauseGesamt, 2, ',', '') . ' Stunde(n)' ?></output></label>
                  		<?php if (count($pausen) > 1) : ?>
                  		<div id="zeitDetail"><span></span><output><?= implode(' ', $pausen) ?></output></div>
                  		<?php endif ?>
                  		<?php endif ?>
                  		<label class="zeitRow"><span>Ende:</span><output><?= htmlspecialchars($ende) ?> Uhr</output></label>
                  

                  Ich dachte label nimmt man nur bei einem Formular?

                  1. Hallo Bernd,

                    Label nimmst Du zum Beschriften. Von Formular-Elementen, oder von output-Elementen.

                    Rolf

                    --
                    sumpsi - posui - clusi
      2. Aber mir geht es erst einmal um das Zusammenfassen in der Schleife wenn es mehr als ein Eintrag für die Pause gibt.

        Das ist jetzt mal der Punkt an dem ich Dir SEHR DRINGEND nahelegen möchte, Dich mit Klassen und Objekten zu befassen. Du versuchst immer alles auf einmal zu tun (hier: Ausgabe und Addieren der Pausen), das setzt sich bis ins HTML fort:

        <span style="display: inline-block; width: 7.4em;">
        

        … lese ich in Deinem kleinem Codestückchen 4 Mal. Das gehört auch in HTML als Klasse definiert und dann im CSS formatiert, allein schon wegen späterer Änderungen.

        Das Objekt könnte die Daten einsammeln und die Summen bilden. Dann holst Du alles was Du brauchst einfach wann Du es brauchst aus dem Objekt.

        Da wäre noch:

        <?php echo htmlspecialchars($arraySEP['uss_inhalt']); ?> Stunde(n)
        

        Da stehen Zahlen drin. Offensichtlich als Text (Mit endenden Nullen bei den Nachkommastellen). Es gibt für solche Ausgaben printf und sprintf

        <?=sprintf( '%01.2f Stunden', $arraySEP['uss_inhalt'] ); ?>
        

        kann aber (auch) Zahlen wunderbar verarbeiten. Wieso fummelst Du vorher mit Text herum? Man muss und soll anno 2018 nicht mehr so programmieren wie anno 1993 auf einem Sharp-Pocketcomputer, C64 oder KC85 mit BASIC.

        <?php
        setlocale (LC_ALL, 'de_DE.UTF-8');
        ?><?=sprintf( '%01.2f Stunde(n)', 2.0000 ); ?> 
        

        Ergebnis:

        2,00 Stunde(n)
        
        1. Hallo ursus,

          number_format geht auch ohne locale.

          Auf meinem PC muss ich als locale de-DE, nicht de_DE angeben, das ist unter Windows und Linux scheinbar unterschiedlich oder Du hast Dich vertippt. In der PHP Sandbox klappt setlocale weder mit de_DE noch de-DE, da scheint dieses locale gar nicht installiert.

          Rolf

          --
          sumpsi - posui - clusi
          1. Hallo ursus,

            number_format geht auch ohne locale.

            Mit dem default (bei mir wohl "en_US.UTF-8").

            Auf meinem PC muss ich als locale de-DE, nicht de_DE angeben, das ist unter Windows und Linux scheinbar unterschiedlich oder Du hast Dich vertippt.

            Nein:

            In der PHP Sandbox klappt setlocale weder mit de_DE noch de-DE, da scheint dieses locale gar nicht installiert.

            Ja. Wenn man Internationalisierung zu nutzen wünscht, dann sollte man im Betriebssystem dafür auch Unterstützung haben. Folgende Linuxbefehle sind also wichtig:

            locale -a;
            

            … gibt unter Linux Auskunft was alles installiert und also verfügbar ist. Achte darauf diese auch mit dem Zusatz für die Kodierung zu verwenden, sonst wird das nichts. Also z.B. "de_DE.UTF-8", "de_DE.utf8" tut es in PHP dann auch, "de_de.utf8" aber nicht...

            apt install locales-all;
            

            … installiert alle locale.

            locale -a | grep -iP '^de'
            

            … zeigt die verfügbaren deutschen Versionen an. Bei mir (ohne Paket locales-all) sind das:

            de_AT.utf8
            de_BE.utf8
            de_CH.utf8
            de_DE.utf8
            de_IT.utf8
            de_LI.utf8
            de_LU.utf8
            

            auf dem Webserver:

            de_AT
            de_AT@euro
            de_AT.utf8
            de_BE
            de_BE@euro
            de_BE.utf8
            de_CH
            de_CH.utf8
            de_DE
            de_DE@euro
            de_DE.utf8
            de_IT
            de_IT.utf8
            de_LI.utf8
            de_LU
            de_LU@euro
            de_LU.utf8
            

            Auf einem professionell betriebenen Webserver sollten aus obigem Grund alle locale vorhanden sein. Falls nicht: Den Support terrorisieren.

            1. Hallo ursus,

              Ja. Wenn man Internationalisierung zu nutzen wünscht, dann sollte man im Betriebssystem dafür auch Unterstützung haben.

              Auf einem professionell betriebenen Webserver sollten aus obigem Grund alle locale vorhanden sein. Falls nicht: Den Support terrorisieren.

              Mag ja sein. Aber die PHP Sandbox ist ein kostenloser Dienst, den irgendwer im wilden weiten Web bereitstellt. Ich würde dem Anbieter gerne auf Knien für diese Seite danken. Man kann basteln, und vor allem gegen eine Menge PHP Versionen testen ob es da so läuft wie gedacht. Aber ich hab's ihm mal als Kommentar dagelassen 😀

              Rolf

              --
              sumpsi - posui - clusi
              1. Aber die PHP Sandbox ist ein kostenloser Dienst, den irgendwer im wilden weiten Web bereitstellt. Ich würde dem Anbieter gerne auf Knien für diese Seite danken. Man kann basteln, und vor allem gegen eine Menge PHP Versionen testen ob es da so läuft wie gedacht.

                Naja. Meine "Sandbox" heisst entweder "localhost", "/tmp/" oder "virtuelle Maschine", manchmal "Banana Pi". Hier mal das Ergebnis meines Bemühens nach dieser Diskussion auf dem banana und auf dem "richtigen Webserver".

            2. Auf einem professionell betriebenen Webserver sollten aus obigem Grund alle locale vorhanden sein.

              Wozu das denn? Locale sind für HTTP völlig irrelevant! Formate für Datum+Zeit beispielsweise sind in einschlägigen RFCs geregelt.

              MfG

              1. Hallo pl,

                es ging um PHP, nicht um HTTP. Und da sind sie absolut relevant für eine lokalisierte Generierung der Ausgaben.

                Rolf

                --
                sumpsi - posui - clusi
                1. @Rolf B

                  es ging um PHP, nicht um HTTP. Und da sind sie absolut relevant für eine lokalisierte Generierung der Ausgaben.

                  Nun, wenn Server und Client auf einer lokalen Maschine laufen mag das zutreffend sein. Ansonsten ist, von PHP mal ganz abgesehen, einem Client herzlich egal welche Zeitzone z.B. auf dem Server eingestellt ist oder welche Zeichenkodierung in der bash eingestellt ist.

                  Das Thema hatten wir übrigens auch schon hier vor einiger Zeit. Estaunlich wie doch längst geklärte Sachverhalte immer wieder hochpoppen, also von denen die damals auch dabei waren.

                  MfG

                  1. Hallo pl,

                    keine Ahnung warum Du das hochpoppst. Hast Du einen akuten Zustand von Impetus Scribendi?

                    Es gibt, wenn ich das richtig sehe, keine Möglichkeit, in der php.ini für die String-Funktionen von PHP ein default locale einzustellen. Wenn man die default locale des Servers nutzen will, muss man setlocale(LC_wasauchimmer, "") aufrufen. Wenn ich von einem amerikanischen Server, der vermutlich per Default en_US (oder en-US) als locale zum Formatieren von Zahlen oder Datümern verwendet, eine deutsche Formatierung will, dann muss ich ihm das sagen.

                    D.h. ohne setlocale-Aufruf bin ich beim Default, und der enthält nur den Dezimalpunkt und sonst nichts. Ob der Server da auf meiner Kiste läuft oder im eisgekühlten Rechenzentrum in Island, ist völlig wurscht.

                    Wobei ich bei diesem sandbox-Server von onlinephpfunctions.com gerade gar nichts mehr zutraue, der liefert mir auch nach Aufruf von setlocale(LC_ALL, "") nur ein leeres localeconv-Array.

                    Mit einer Zeichencodierung in der bash (womit Du vermutlich Einflüsse der Default-Locale des Servers meinst) hat das alles jedenfalls nichts zu tun. Die Zeitzone, mit der die PHP Anwendung dem Client Zeitangaben formatiert, ist was anderes, aber die hängt nicht von setlocale ab, sondern von date_default_timezone_set und ähnlichem (je nach verwendetem Funktionenset).

                    Rolf

                    --
                    sumpsi - posui - clusi
                  2. Nun, wenn Server und Client auf einer lokalen Maschine laufen mag das zutreffend sein. Ansonsten ist, von PHP mal ganz abgesehen, einem Client herzlich egal welche Zeitzone z.B. auf dem Server eingestellt ist

                    Das Software-Paket "locales-all" (bzw. die Teilpakete) liefern Templates für die Formatierung von Zahlen, Währungs- und Zeitangaben. Die werden von C und CPP-Programmen genutzt. Und dazu gehört PHP insbesondere eben dessen häufig genutzte Funktionen sprintf und printf.

                    Freilich kann (und sollte) man auch die php-intl-lib nutzen. Die macht PHP von den installierten locales unabhängig.

                    1. Das Software-Paket "locales-all" (bzw. die Teilpakete) liefern Templates für die Formatierung von Zahlen, Währungs- und Zeitangaben. Die werden von C und CPP-Programmen genutzt. Und dazu gehört PHP insbesondere eben dessen häufig genutzte Funktionen sprintf und printf.

                      • Perl tut das auch:

                      "[sprintf] Returns a string formatted by the usual printf conventions of the C library function sprintf."

                      … und ist ergo auch von den locales-Templates abhängig.

  2. Führe Zeitarten ein. Z.B. Pause, Produktion, Dienstreise, Urlaub usw. Dann kriegst Du bei einer Abfrage auf einen gewünschten Zeitraum alldiese Zeitarten lückenlos aneinandergereiht, z.B. als Objekte in einer verketteten Liste [{ZA:Prod,von:0600,bis:0800},{ZA:Pause,von:0800,bis:0815}] usw.

    Das schickst Du alles zum Browser, macht damit ein View und wenn der Anwender Details sehen will machst Du das mit Klickevents entsprechend sichtbar (Templates umschalten).

    Alternative zu JS: Verschiedene Views auf verschiedene Parameter abbilden, Templates serverseitig rendern.

    MfG