nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.
Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance. Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.
Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.
Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.
Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder einen alternativen Browser verwenden.
ich hab eigentlich ein ziemlich simples Problem. Lese eine große Datenbank aus. Nun prüfe ich per
[src=php]
if in_array("X", $zeile) {
[mach wat]
}
[/src]
ob in den Spalten der Wert X vorhanden ist. Nun will ich das aber nicht am Stück ausgeben sondern gepaart zu passenden Gruppen. Klar, ich könnte nun auch pro Gruppe eine eigene Abfrage machen und diese dann so ausgeben, wenn ich die Abfrage aber schon habe, dachte ich, ich könnte die dann ja weiter verwenden.
Mir kam auch die Idee das ich in in_array() ja nach einem neuen array filtern könnte. Quasi so:
[src=php]
$filter = array('a', 'b', 'c', 'd');
if in_array($filter, $zeile) {
[mach wat]
}
[/src]
Das scheint aber nicht zu klappen. Laut php.net -> Beispiel #3 in_array() mit Array als Suchwert sollte das doch aber klappen!?
Ja, $zeile ist ein Array. Kommt von meiner Haupt-DB abfrage. In_array() funktioniert ja auch wunderbar wenn ich z.B. nach "X" suche. Ich will aber mehrere Suchbegriffe dafür hernehmen. Laut php.net sollte also ein zweiter array() funktionieren. Oder hab ich da irgendwo einen Denkfehler?
kann leider gerade nicht weiter schauen da ich im Zug sitze
Das klappt dort, weil der zweite Parameter ein zweidimensionales Array ist. Es wird also überprüft, ob das Array aus dem ersten Parameter vollständig im zweiten Array enthalten ist. Insofern ist das wieder das gleiche wie das erste Beispiel mit dem String, nur mit Arrays als innerer Datentyp.
Ich verstehe Dein Vorhaben allerdings ohnehin nicht so ganz. Willst Du abfragen, ob die Werte in der Zeile vorhanden sind, unabhängig davon, aus welcher Spalte sie stammen? Wäre es nicht besser, die Spalten direkt abzufragen?
Wie auch immer, für Dein Vorhaben fallen mir zwei Möglichkeiten ein.
1. Du könntest Dir einfach eine Hilfsfunktion schreiben:
PHP:
function all_in_array($needle, $haystack){
foreach($needle as $value){
if(!in_array($value, $haystack)){
return false;
}
}
return true;
}
2. Du prüfst, ob die Schnittmenge der beiden Arrays die gleiche Anzahl an Elementen hat wie Dein Filter:
PHP:
if(count(array_intersect($zeile, $filter)) == count($filter)) {
// mach wat
Ich verstehe Dein Vorhaben allerdings ohnehin nicht so ganz. Willst Du abfragen, ob die Werte in der Zeile vorhanden sind, unabhängig davon, aus welcher Spalte sie stammen? Wäre es nicht besser, die Spalten direkt abzufragen?
Habe eine Webseite mit DB, aus den Daten die ich einerseits für die Webseite nutze möchte ich im Backend per HTML5 eBooks erstellen (Parameter an der URL bestimmen hier welche Infos in die eBooks kommen). Die Datenbank besteht aus ~ 260 Spalten. Ein paar enthalten Texte, die meisten fungieren aber nur als Ja/Nein-Felder (X = Ja, kein X = Nein) und zahlen (Fließkomma) sowie Ja/Nein + Fließkommazahlen. Nun sind die Ausgaben welche ich für meine HTML5-Ausgabe benötige in verschiedene Blocks unterteilt. Das $zeile[] Array enthält einfach die komplette Abfrage der DB. Bei der Ausgabe werden jetzt also auch Spalten ausgegeben die bei Zeile XY keinen Wert (X oder Zahl) enthalten. Sprich die leer sind.
Diese Spalten wollte ich im Vorfeld mit dem $filter herausfiltern ($name ist der Spaltenname meiner DB). So das ich pro Block einfach angeben kann wlche Spalten er in meiner folgenden foreach-Schleife ausgeben kann. Das ganze werde ich jetzt wohl aber einfach mit mehreren SQL-Abfragen lösen. $sql_block1, $sql_block2, $sql_block3 usw. So habe ich dann einfach schon anfangs die korrekten Spalten die ich dann nach in_array("X", $sql_block1) usw. durchsuchen kann. Habe mir nur gewundert wieso das mit dem zweiten Array nicht geklappt hat.
also quasi
[src=php]$sql = "SELECT tabelle1.*, tabelle2.* FROM tabelle1 LEFT JOIN tabelle2 ON tabelle1.url = tabelle2.url WHERE tabelle1.aktiv='1' AND tabelle1.url='".mysql_real_escape_string($url)."'";[/src]
weiter unten will ich dann nicht alle spalten ausgeben sondern halt nur z.b. spalte 4 bis 9, 12, 25 bis 40. Darum wollte ich die Spaltennamen per Array/Filter in die in_array() Funktion quetschen.
So brauche ich ja für jeden Block eine eigene Abfrage wo ich die Spalten auch explizit in der SQL-Abfrage angebe...
Ok, hab es gelöst. Eigentlich simpel, keine Ahnung wieso ich da nicht gleich drauf gekommen bin
Ich habe ja 2 Arrays. Ein Array wird aus dem Query erzeugt -> $zeile, einen zweiten definiere ich ja selbst -> $filter
Da ich ja die Schnittmenge für meine Block-Ausgabe benötige vergleiche ich die zwei Arrays einfach mit array_intersect_assoc($filter1, $zeile). Da ich davor per in_array("X", $zeile) nur Inhalte mit "X" zulasse, reicht es meinem $filter-Array jeweils noch "X" zuzuweisen:
[src=php]
$sql = "SELECT * FROM db WHERE ...";
$erg = mysql_query($sql);
if (!$erg) {
die('Ungültige Abfrage: ' . mysql_error());
}
$zeile = mysql_fetch_array($erg, MYSQL_ASSOC);
if (in_array("X", $zeile)) {
$fields = array_intersect_assoc($filter, $zeile);
foreach (($fields ) as $name => $value) {
echo $name;
}
}
[/src]
So werden also nur die Spalten ausgegeben welche 1. einen Wert X besitzen und zweitens ebenso in meinem Filter-Array vorkommen. Sprich ich kann mit verschiedenen Filter-Arrays gewünschte Spalten/Blöcke ausgeben. Genau das was ich wollte
hatte mit deiner Funktion gestern schon getestet, aber nie eine Ausgabe bekommen. Wie muss $needle denn aufgebaut sein?
Die Funktion läuft im Übrigen in einer while-Schleife ab da ich pro Datensatz aus der DB andere Spalten filtern muss (Datensatz 1 hat z.B: bei a, c, f, und p ein X. Datensatz b bei a, b, f usw. Habe folgendes getestet:
Ich habe also zwei Arrays. $needle und $heystack. Ich benötige einerseits alle Schlüssel + alle Werte. Ich bekomme aus der DB ja eine Ausgabe die ~ so ausieht:
Ich brauche dann quasi alle Schlüssel bei denen die Werte bei beiden Arrays sich überschneiden. (Die Schlüssel sind außerdem die Spaltenbezeichnungen der DB und gleichzeitig die Namen der Grafiken die ich am Ende ausgebe.
Bei meinem Beispiel wäre das folgendes:
[src=php]Array ( [a] => X [d] => [e] => X [f] => )[/src]
wenn ich nach "X" filtere bleibt noch a und e über. Einzig wegen den Werten größer 0 muss ich mal noch schauen. Das klappt in meiner Version auch noch nicht.
Mit deinem Beispiel komme ich hier irgendwie nicht weiter. Ich will ja kein bestimmtes "value" filtern sondern einen ganzen Array.
Oder ich steh bei deiner Funktion auch einfach auf dem Schlauch
Prinzipiell kannst du so auch einen komplexen Filter bauen, der auch die Werte in Betracht zieht.
Zum Beispiel mit Closures... und statischen Factories die häufig genutzte Closures produzieren.
PHP:
class Filter {
public static function eq($y) {
return function($x) use ($y) {
return $x === $y;
};
};
public static function in($y) {
return function($x) use ($y) {
return in_array($x, $y);
};
};
public static function num() {
return function($x) {
return is_numeric($x);
};
};
private $config = [];
public function configure($config) {
$this->_config = $config;
}
public function __invoke($array) {
$result = [];
foreach($this->config as $key => $validator) {
if (isset($array[$key]) && $validator($array[$key])) {
$result[$key] = $array[$key];
}
}
return $result;
}
}
Die Werte sind dabei für mich zwingend notwendig. Die Spalte ist später für die angezeigte Grafik zuständig, der Wert bestimmt ob und wieso diese angezeigt wird.
Prinzipiell kannst du so auch einen komplexen Filter bauen, der auch die Werte in Betracht zieht.
Zum Beispiel mit Closures... und statischen Factories die häufig genutzte Closures produzieren.
while ($zeile = mysql_fetch_array($erg, MYSQL_ASSOC)) {
$array2 = array('a' => 'X', 'b' => $zeile['b'], 'c' => $zeile['c'], 'd' => '', 'e' => 'X', 'f' => '', 'g' => '', 'h' => $zeile['h']);
// die $zeile['xyz'] sind in der while schleife da sich diese felder bei jedem Datensatz ändern
$fields = array_intersect_assoc($array2, $zeile);
foreach (($fields ) as $name => $value) {
if (($value > 0) or ($value == 'X')) {
echo '<img src="http://www.domain.de/gfx/symbole/'.$name.'.png"> ';
}
}
}[/src]
Performance ist nicht unbedingt eine Frage der Codezeilen ;-) Aber: solang das array nicht tausende von Einträgen hat ist das eh eine Microoptimierung, sofern nicht unsinnige innere Schleifen drin sind. In deinem Fall ist es performancetechnisch sicher wichtiger die Zahl der DB Abfragen gering zu halten.
Klar, ist schließlich mein Code Spaß bei Seite: mein Ziel war es eine leicht anpassbare und möglichst generische Filterlogik zu haben. Ihre Stärke zeigt sie dann, wenn sie häufig wieder verwendet wird -> Filter configurieren und ausführen. fertig.
Genau deswegen wollte ich ja auch mehrere DB-Abfragen verzichten und das ganze so lösen wie ich eben jetzt mache.
PS: Habe zwischenzeitlich noch mit einer Bekannten geredet. Die hat mir folgendes noch vorgeschlagen:
[src=php]$newArray = array();
foreach ($array1 as $name => $value) {
if(isset($array2[$name])) {
if($array1[$name] == $array2[$name]){
$newArray[$name] = $value;
if (($value > 0) or ($value == 'X')) {
echo '<img src="http://www.domain.de/symbole/'.$name.'.png"> ';
}
}
}
}
[/src]
Klappt auch. Allerdings muss ich hier den Array wieder umdrehen da er quasi rückwärts raus kommt.
Danke schon mal für dein Beispiel, bedenke das ich hier nur mit kleinen Beispielen zeigen wollte was ich vor habe. Mein echter $array1 ist ~ 260 Einträge lang. Da kommt in unregelmäßigen abfolgen alles kreuz und quer vor. Fließkommazahlen, X, (X + Fließkommazahl, X ohne Fließkommazahl, X und Leer [da muss ich jeweils zwei Spalten abfragen die nur in Kombination ein TRUE oder FALS ergeben dürfen) usw. Meinst du es macht Sinn das alles in deinen CallbackFilter() zu packen? Im Endeffekt bleibt die (weitgehend echt beschissene) Datenbankstruktur die Selbe, daran kann ich leider auch nichts ändern. Ich muss daraus einfach ~4-5 Blöcke an Daten ziehen die ich eben verschieden Filtern wollte.
Bei den zwei Beispiel-Mini-Arrays sieht dein Beispiel in der Tat bequem aus, ich befürchte aber das ich mir mit meinem Quell-Array ein wahres Monstrum zusammen bauen werde
Das ist der Punkt, für den ich dieses Design gut fand: Man deklariert die Spalte und Logik wie die Werte darin zu behandeln ist und bekommt den Teil zurück, für den die Logik true ausgibt.
Natürlich kann es andere Dinge nicht: Paarweise Validierung: Spalte a nur ausgeben, wenn a und b beide X sind.
Oder Blockauswahl: Wenn Spalte a gleich X dann selektiere a,b,c und d.
Also zurück ans Reißbrett. Neuer Ansatz ist kein Filter, sondern ein Selector.
public function configure($config) {
$this->_config = $config;
}
public function __invoke($array) {
$result = [];
foreach($this->config as list($selector, $selection)) {
$result += $selector($array, array_flip($selection)));
}
return array_intersect_key($array, $result);
}
}[/src]! foreach(... as list(...)) ist PHP 5.5 !
So nun zu den Closures. Deren Interface hat sich geändert: sie bekommen jetzt das Array und ihre Selection (Performance siehe: copy on write) und müssen ein array ausgeben. Der $key der Config spielt keine Rolle mehr stattdessen muss ein Selector und seine Selection angegeben werden.
Blöcke anhand einer X Spalte auswählen:
[src=php]/**
* @return Closure a selector that fires if the key column is 'X'
*/
public static function ifX($key) {
return function ($array, $selection) use ($key) {
return (isset($array[$key]) && $array[$key] === 'X') ? $selection : [];
}
}[/src]
Blöcke anhand einer X Spalte und einer Float Spalte auswählen
[src=php]/**
* @return Closure a selector that fires if the $x column is 'X' and the $f column is > 0;
*/
public static function ifXF($x, $f) {
return function ($array, $selection) use ($x, $f) {
return (isset($array[$x]) && $array[$x] === 'X' && isset($array[$f]) && $array[$f] > 0) ? $selection : [] ;
}
}[/src]
Bisher haben die Selectoren die Selection nur durchgeschleust.
[src=php]/**
* @return Closure a selector that returns all columns in selection that are 'X'
*/
public static function anyX() {
return function ($array, $selection) {
return array_filter(array_intersect_key($array, $selection), function($value) { return $value === 'X'; });
}
}[/src]
[src=php]/**
* @return Closure a selector that returns pairwise true columns in selection
* Requires an array of pairs as selection - ! unusual interface !
*/
public static function pairs() {
return function ($array, $selection) {
$result = [];
foreach($selection as list($x,$f)) {
if (isset(array($x)) && array($x) && isset(array($x)) && array($x)) {
$result += [$x => array($x), $y => array($y)];
}
return $result;
}
}[/src]
Nur so nebenbei: X oder Float oder XFloat aber nicht '', false und 0 lässt sich mit einem cast auf bool sehr einfach prüfen ;-)
Aber weil die Idee ja nicht ist, dass der Code lesbar ist, frisst dieser Einzeiler String und String[].
[src=php]public static function ifTrue($columns){return function ($array, $selection) use ($columns) {return (is_string($columns) && isset($array[$columns]) && $array[$columns]) || (is_array($columns) && array_reduce($columns,function($carry, $c) { return $carry &= (isset($array[$c]) && $array[$c]);},true)) ? $selection : [];}}[/src]
j.k.[src=php]/**
* @param string[] $columns or one column name as a string.
* @return Closure a selector that fires if all $columns cast to true
*/
public static function ifTrue($columns) {
return function ($array, $selection) use ($columns) {
return (
is_string($columns) && isset($array[$columns]) && $array[$columns]
) || (
is_array($columns) && array_reduce($columns,
function($carry, $c) { return $carry &= (isset($array[$c]) && $array[$c]);},
true
)
) ? $selection : [];
}
}[/src]
Configuration: Die Grundidee ist, dass die Config 'lesbar' ist. pairs() gefällt mir nicht, sagt nichts darüber aus, wie die Paare geprüft werden. Aber bothIfBothTrue() mag ich auch net.
[src=php]$select = new CallbackSelector();
$select->configure([
[$select::ifX('a'), ['b','c','d']],
[$select::ifXF('e','f'), ['e']],
[$select::anyX(), ['g','h']],
[$select:airs(), [['ö','ä'],['q','p']],
[$select::ifTrue('fun'), ['n','g','b']]
]);[/src]
Die Selektionen können sich überschneiden: auch wenn g nicht X ist, wenn $array['fun'] == true, dann n, g, b!
Ich hoffe ich konnte dich mit etwas late night coding inspirieren. Sinn macht das ganze configure Zeug natürlich nur, wenn du mehrere Configurationen hast. Und auch die Closures werden erst dann richtig nützlich, wenn man für eine spezielle Config neue Logik injizieren will, ohne den CallbackSelector selbst zu verändern.
Rant:
Man das ganze auch ausbreiten:
[$select::ifX('a'),...] wird zu $this->ifX('a', ['b','c','d']) und statt Closure Factories gibt es private Methoden. Dann werfen wir das configure weg und die __invoke wird zur statischen filter($array) die zeilenweise die in Funktionsaufrufe umgewandelte config enthält.
Es ist immernoch lesbar und für Dinge wie Wiederverwertbarkeit und Erweiterbarkeit gibt es ja Vererbung (statt dem vereinfachtem Strategy-Pattern). Außerdem ist das eh voll überbewertet: Warum Code schreiben, der wiederverwertbar ist, wenn man nicht plant ihn in noch mal woanders zu verwenden? Ist genau wie Generalisieren: Viel zu aufwendig um ein konkretes Problem zu lösen.
Schließlich kann man noch die Funktionsaufrufe durch deren Body ersetzen und die nutzlose Klasse ist weg, es bleibt filter($array). Und jenen Body dann einfach zwischen die Datenbanklogik in die while schieben. Bei 260 Spalten können wir neben Wiederverwertbarkeit und Erweiterbarkeit dann auch mal über Wartbarkeit reden ;-)
Ich weiß nicht, ob dir all das etwas nützt, aber ich hatte auf jeden Fall meinen Spaß daran Gute Nacht.
Weil ihr euch alle bedankt habt, will ich den im Rant beschriebenen Weg mal gehen
[src=php]function filter($array) {
$select = [];
if ($array['someColumnNameGoesHere'] === 'X') $select += [
'Configs sind was schönes'
'denn wenn einen der Code beim Lesen des Codes stört',
'dann schreibt man ihn halt woanders hin'
];
$select += array_filter([
'Ihr habt euch nicht ernsthaft,'
'mit Wiederverwendbarkeit Ködern lassen, oder?'
'Wo ist denn bitte die Wiederverwendbarkeit'
'der Closure Factory ifX?',
], function($key) use (&$array) {
return ($array[$key] === 'X' || $array[$key] > 0);
});
if ($array['thisOneIsX'] === 'X' || $array['andThisOneIsFloat'] > 0) $select += [
'und was die Generalisierung angeht:'
'wohin soll ich ifX denn generalisieren',
'wenn nicht zu if($condition)',
];
Mit ausnahme von X ist alles was rot ist ein Spaltenname. -> voll unübersichtlich
Und dann erst diese kopfgesteuerten if Blöcke in einer Funktion mit ner fußgesteuerten array_filter.. geht gar nicht, das versteht doch keiner mehr.
Generalisieren ist toll, aber wenn den Lösungansatz soweit generalisiert hat, dass man das ursprüngliche Problem jetzt in einer anderen, weniger mächtigen Sprache lösen muss.. macht man was falsch ;-)
In dem Sinne habe ich mir für euch noch was schönes ausgedacht:
[src=php]class ArrayDB {
protected $from;
protected $selection;
protected $result;
public function __construct($from){
$this->from = $from;
$this->selection = [];
$this->result = [];
}
public function select($select) {
$this->selection = array_flip($select);
return $this;
}
public function when($condition) {
if($condition) $this->result += array_intersect_key($this->from, $this->selection);
}
public function filter($closure) {
$this->result += array_filter(array_intersect_key($this->from, $this->selection), $closure);
}
public function getResult() {
return $this->result;
}
}[/src]
Dank dieser tollen Klasse kann man das Problem jetzt mit einer SQL ähnlichen Syntax lösen
[src=php]$filter = function($zeile) {
$adb = new ArrayDB($zeile);
$adb->select(['a', 'b'])
->when($zeile['a'] == 'X');
$adb->select(['c', 'd'])
->when($zeile['e'] == 'X' && $zeile['f'] > 0);
$adb->select(['g', 'h', 'j'])
->filter(function($v) {return $v === 'X' || $v > 0;});
$adb->select(['n', 'g', 'b'])
->when(true);
return $adb->getResult();
}[/src]