WordPress-Überschriften und -Kategorien, Funktion wp_list_categories. PHP-Klasse für bequemes und sicheres Arbeiten mit MySQL. Nutzloses PHP der Kategorie

In diesem Artikel (Webmaster-Level – Fortgeschritten) werden wir auf unterschiedliche Weise über das sogenannte Intersecting sprechen. „facettierte“ Navigation. Um die Aufnahme des Materials zu vereinfachen, empfehle ich die Lektüre des Wikipedia-Artikels „Facettenklassifizierung“ und der Veröffentlichungen dazu Englisch(aber mit Bildern!) „Entwerfen Sie eine facettenreichere Navigation für Ihre Websites.“

Facettennavigation mit Filterung nach Farbe oder Preisklasse kann für Ihre Besucher nützlich sein, ist jedoch bei der Suche oft schädlich, da dadurch viele Kombinationen von Adressen mit doppeltem Inhalt entstehen. Aufgrund von Duplikaten Suchmaschinen nicht in der Lage sein, die Website schnell nach Inhaltsaktualisierungen zu durchsuchen, was sich folglich auf die Indizierung auswirkt. Um dieses Problem zu minimieren und Webmastern dabei zu helfen, die Suche mit der Facettennavigation benutzerfreundlicher zu gestalten, möchten wir:

Ideal für Benutzer und Google-Suche

Klarer Pfad zu den Produkt-/Artikelseiten:

URL-Darstellung für die Kategorieseite:
http://www.example.com/category.php?category=gummy-candies

Produktspezifische URL-Darstellung:
http://www.example.com/product.php?item=swedish-fish

Unerwünschte Duplikate durch Facettennavigation

Auf dieselbe Seite kann von verschiedenen Webadressen aus zugegriffen werden:

Kanonische Seite



URL: example.com/product.php? item=schwedischer-fisch

Doppelte Seite



URL:example.com/product.php? item=swedish-fish&category=gummy-candies&price=5-10


Kategorie=Gummibonbons&Geschmack=Sauer&Preis=5-10

Fehler:

  • Für Google sinnlos, da Nutzer selten nach [Marmelade zum Preis von 9:55 $] suchen.
  • Für Crawler bedeutungslos, da sie denselben Artikel („Obstsalat“) auf übergeordneten Kategorieseiten finden (entweder „Jummy“ oder „Sour Gummy“).
  • Ein negativer Punkt für den Websitebesitzer, da Indexierungsanfragen durch zahlreiche Versionen derselben Kategorie verwässert werden.
  • Ein negativer Punkt für den Websitebesitzer, da es nutzlos ist und eine zusätzliche Belastung darstellt Bandbreite Website
Leere Seiten:


URL: example.com/category.php? Kategorie = Gummibonbons & Geschmack = Sauer & Preis = über 10

Fehler:

  • Der Code für Suchmaschinen wird falsch zurückgegeben (in diesem Fall sollte die Seite einen 404-Code zurückgeben)
  • Leere Seite für Benutzer


Schlechteste Lösungen (nicht suchfreundlich) für die Facettennavigation

Beispiel Nr. 1: Als Teil der URL werden nicht standardmäßige Parameter verwendet: stattdessen Kommas und Klammern Schlüssel=Wert&:

  • example.com/category? [ Kategorie: Gummibonbons ] [ Sortierung: Preis von niedrig nach hoch ] [ Sid: 789 ]
  • example.com/category?category , Gummibonbons , sort , lowtohigh , sid , 789
So geht's:
example.com/category? Category=Gummibonbons&sort=low-to-high&sid=789

Beispiel Nr. 2: Verwendung von Verzeichnissen oder Dateipfaden anstelle von Parametern in Wertelisten, die den Seiteninhalt nicht ändern:
example.com/c123 /s789/ product?swedish-fish
(wobei /c123/ Kategorie, /s789/ Sitzungs-ID, was den Seiteninhalt nicht ändert)

Gute Lösung:

  • example.com /gummy-candy/ produkt?item=schwedischer-fisch&sid=789(das Verzeichnis /gummy-candy/ verändert den Inhalt der Seite sinnvoll)
Beste Lösung:
  • example.com/product?item=swedish-fish& Kategorie=Gummibonbons&sid=789 (URL-Parameter bieten Suchmaschinen mehr Flexibilität bei der Bestimmung, wie sie effektiv crawlen)
Für Crawler ist es schwierig, nützliche Werte (z. B. „Gummy-Candy“) von nutzlosen Werten (z. B. „SESSIONID“) zu unterscheiden, wenn diese Werte direkt in Linkpfaden platziert werden. Andererseits bieten URL-Parameter den Suchmaschinen die Flexibilität, schnell zu testen und festzustellen, wann ein bestimmter Wert keinen Crawler-Zugriff auf alle Optionen erfordert.

Zu den gängigen Werten, die den Inhalt der Seite nicht verändern und als URL-Parameter aufgeführt werden müssen, gehören:

  • Sitzungs-ID
  • ID-Verfolgung
  • Referrer-IDs
  • Zeitstempel
Beispiel Nr. 3: Konvertieren Sie vom Benutzer generierte Werte (möglicherweise unendlich) in URL-Parameter, die crawlbar und indizierbar, aber für die Suche unbrauchbar sind.
Verwendung kleinerer, von Website-Benutzern generierter Daten (z. B. Längen-/Breitengrad oder „vor Tagen“) in gecrawlten und indizierten URLs:
  • example.com/find-a-doctor? Radius=15&Breitengrad=40,7565068&Längengrad=-73,9668408
  • example.com/article?category=health& vor Tagen=7
So geht's:
  • example.com/find-a-doctor? Stadt=San-Francisco&Neighborhood=Soma
  • example.com/articles?category=health& Datum = 10. Januar 2014
Anstatt dem Benutzer zu erlauben, Werte zu generieren, um crawlbare URLs zu erstellen (was zu endlosen Möglichkeiten mit sehr geringem Wert für Besucher führt), ist es besser, eine Seitenkategorie für die beliebtesten Werte zu veröffentlichen, zusätzlich können Sie diese einbeziehen Weitere Informationen sodass die Seite mehr Wert bietet als eine normale Suchergebnisseite. Alternativ können Sie erwägen, benutzergenerierte Werte in einem separaten Verzeichnis abzulegen und dann robots.txt zu verwenden, um das Crawlen aus diesem Verzeichnis zu verhindern.
  • example.com /filtern/ find-a-doctor?radius=15&latitude=40.7565068&longitude=-73.9668408
  • example.com /filtern/ Articles?category=health&days-ago=7
Und in robots.txt:
Benutzeragent: *
Nicht zulassen: /filtern/

Beispiel Nr. 4. URL-Parameter ohne Logik hinzufügen.

  • example.com /Gummibonbons/Lutscher/Gummibonbons/ Gummibonbons/Produkt?schwedischer-Fisch
  • example.com/product? cat=Gummibonbons&cat=Lollipops&cat=Gummibonbons&cat=gummibonbons&item=schwedischer-fisch
Gute Lösung:
  • example.com /gummy-candy/ product?item=swedish-fish
Beste Lösung:
  • example.com/product? item=swedish-fish&category=gummy-candy
Überflüssige URL-Parameter erhöhen nur die Duplizierung und führen dazu, dass die Website weniger effizient gecrawlt und indiziert wird. Daher ist es notwendig, unnötige URL-Parameter zu entfernen und Junk-Links regelmäßig zu bereinigen, bevor neue URLs generiert werden. Wenn für eine Benutzersitzung viele Parameter benötigt werden, können Sie die Informationen in Cookies ausblenden, anstatt ständig Werte hinzuzufügen wie cat=Gummibonbons&cat=Lollipops&cat=Gummibonbons& ...

Beispiel Nr. 5: Schlagen Sie weitere Verfeinerungen (Filterung) vor, wenn keine Ergebnisse vorliegen.

Schlecht:
Ermöglichen Sie Benutzern die Auswahl von Filtern, wenn keine Elemente zum Verfeinern vorhanden sind.


Klärung einer Seite mit null Ergebnissen (z. B. Preis = über 10), was Benutzer frustriert und unnötige Anfragen an Suchmaschinen verursacht.

So geht's:
Erstellen Sie Links nur, wenn Elemente vorhanden sind, die der Benutzer auswählen kann. Wenn das Ergebnis Null ist, wird der Link „grau“ markiert (d. h. nicht anklickbar). Um die Benutzerfreundlichkeit weiter zu verbessern, sollten Sie erwägen, neben jedem Filter einen Indikator für die Anzahl der verfügbaren Elemente einzufügen.


Das Anzeigen einer Seite mit null Ergebnissen (z. B. Preis = über 10) ist nicht zulässig. Außerdem ist es Benutzern untersagt, unnötige Klicks durchzuführen, und Suchmaschinen dürfen diese nicht nützliche Seite nicht crawlen.

Es ist notwendig, das Erscheinen unnötiger Adressen zu verhindern und den Platz für den Besucher zu minimieren, indem URLs nur dann erstellt werden, wenn Produkte verfügbar sind. Dies trägt dazu bei, dass die Nutzer weiterhin auf Ihrer Website bleiben (weniger Klicks auf die Zurück-Schaltfläche, wenn keine Produkte gefunden werden) und verringert die Anzahl möglicher URLs, die Suchmaschinen bekannt sind. Wenn eine Seite nicht nur „vorübergehend nicht vorrätig“ ist, sondern wahrscheinlich auch nie relevante Informationen enthalten wird, sollten Sie darüber hinaus in Erwägung ziehen, ihr einen 404-Antwortcode zu geben. Auf der 404-Seite können Sie erstellen nützliche Nachricht für Benutzer mit eine große Anzahl Optionen in der Navigation oder im Suchfeld, damit Benutzer verwandte Produkte finden können.

Für neue Websites, deren Webmaster die Implementierung einer Facettennavigation in Betracht ziehen, gibt es mehrere Optionen, um das Crawling (den Satz von Adressen auf Ihrer Website, die Google bekannt sind) einzigartiger Inhaltsseiten zu optimieren und zu verhindern, dass doppelte Seiten in den Suchmaschinenindex gelangen (Konsolidierung der Indexierung). Signale).

Bestimmen Sie, welche URL-Parameter erforderlich sind, damit Suchmaschinen jede einzelne Inhaltsseite crawlen können (d. h., welche Parameter erforderlich sind, um mindestens einen Klickpfad zu jedem Element zu erstellen). Erforderliche Parameter können Artikel-ID, Kategorie-ID, Seite usw. sein.

Bestimmen Sie, welche Parameter für Besucher bei ihren Abfragen nützlich sind und welche wahrscheinlich zu Duplikaten beim Crawlen und Indexieren führen. Im Süßwarenbeispiel (Marmelade) könnte der URL-Parameter „geschmack“ für Benutzer mit Anfragen im Beispiel wertvoll sein Geschmack = sauer . Es ist jedoch logisch, dass der Parameter „Preis“ unnötige Doppelarbeit verursacht Kategorie=Gummibonbons&geschmack=sauer& Preis=über-10 . Weitere häufige Beispiele:

  • Wertvolle Parameter für Suchmaschinen: Artikel-ID, Kategorie-ID, Name, Marke ...
  • Unnötige Parameter: Sitzungs-ID, Preisspanne ...
Schauen wir uns die Implementierung einer von mehreren Konfigurationsoptionen für URLs an, die unnötige Parameter enthalten. Stellen Sie nur sicher, dass die „unnötigen“ URL-Parameter nicht tatsächlich erforderlich sind, damit Suchmaschinen-Crawler sie crawlen oder der Benutzer jedes einzelne Produkt finden kann!

Option 1: und interne Links

Markieren Sie alle unnötigen URLs mit dem . Dadurch werden die Arbeitskosten des Suchroboters gesenkt und eine Verringerung der Crawling-Frequenz verhindert. Sie müssen das Scannen global über robots.txt verwalten (Anmerkung des Übersetzers: siehe Artikel „“).
Verwenden Sie das Attribut rel="canonical", um Seiten zu trennen Suchindex von dort nicht benötigten Seiten (zum Beispiel auf der Seite Preis=5-10 Sie können das Attribut rel="canonical" hinzufügen, das die Kategorie aller sauren Marmeladen angibt example.com/category.php?category=gummy-candies&taste=sour& Seite=alle ).

Option 2: Robots.txt und Disallow

URLs mit unnötigen Parametern sind im Verzeichnis /filtering/ enthalten, das in robots.txt geschlossen wird (nicht zulassen). Dadurch können alle Suchmaschinen nur den „richtigen“ In-Link (Inhalt) der Website crawlen, das Crawlen unerwünschter URLs wird jedoch sofort blockiert. Zum Beispiel ( example.com/category.php?category=gummy-candies), wenn die wertvollen Parameter Artikel, Kategorie und Geschmack wären und die Sitzungskennung und der Preis überflüssig wären, dann würde die URL für Geschmack so aussehen:
example.com/category.php?category=gummy-candies& Geschmack = sauer, aber alle unnötigen Parameter, wie zum Beispiel der Preis, werden in der URL in einem vordefinierten Verzeichnis enthalten – /filtering/:
example.com /filtern/ Category.php?category=gummy-candies&price=5-10,
was dann über robots.txt verboten wird:
Benutzeragent: *
Nicht zulassen: /filtern/

Option 3: Separate Hosts

Stellen Sie sicher beste Lösungen, die oben aufgeführt sind (z. B. für unnötige Adressen), gelten weiterhin. Ansonsten haben Suchmaschinen bereits eine große Linkmasse im Index gebildet. Daher wird Ihre Arbeit darauf abzielen, das weitere Wachstum unnötiger Seiten, die vom Googlebot gecrawlt werden, zu reduzieren und die Indexierungssignale zu konsolidieren.

Verwenden Sie Parameter mit Standardkodierung und Schlüssel=Wert-Format.

Stellen Sie sicher, dass Werte, die den Seiteninhalt nicht ändern, wie z. B. Sitzungs-IDs, als Schlüssel=Wert und nicht als Verzeichnisse implementiert werden.

Lassen Sie keine Klicks zu und generieren Sie keine URLs, wenn keine Elemente zum Filtern vorhanden sind.

Fügen Sie der Zuordnung von URL-Parametern Logik hinzu: Entfernen Sie unnötige Parameter, anstatt ständig Werte hinzuzufügen (vermeiden Sie beispielsweise die Linkgenerierung wie folgt: example.com/product?cat=gummy-candy&cat=lollipops &cat=gummy-candy&item=swedish-fish).

Behalten Sie wertvolle Parameter in der URL bei, indem Sie sie zuerst auflisten (da URLs in den Suchergebnissen sichtbar sind) und weniger relevante Parameter zuletzt auflisten (z. B. Sitzungs-ID).
Vermeiden Sie diese Linkstruktur: example.com/category.php? Sitzungs-ID=123&Tracking-ID=456&category=gummibonbons&geschmack=sauer
Konfigurieren Sie URL-Parameter in den Webmaster-Tools, wenn Sie genau wissen, wie Links auf Ihrer Website funktionieren.

Stellen Sie sicher, dass bei der Verwendung von JavaScript zum dynamischen Bearbeiten von Inhalten (Sortieren/Filtern/Ausblenden) ohne Aktualisierung der URL tatsächliche Webadressen auf Ihrer Website vorhanden sind, die einen Suchwert haben, z. B. Hauptkategorie- und Produktseiten, die gecrawlt und indiziert werden können. Versuchen Sie, nicht nur zu verwenden Homepage(d. h. eine URL) für Ihre gesamte Website und ändern Sie mithilfe von JavaScript den Inhalt dynamisch mit der Navigation. Dies führt jedoch leider dazu, dass Benutzer bei Suchanfragen nur eine URL erhalten. Überprüfen Sie außerdem, dass die dynamische Filterung keine Auswirkungen auf die Leistung hat die schlimmste Seite, da der Benutzer dadurch daran gehindert wird, mit der Website zu arbeiten.

Verbessern Sie die Indizierung verschiedener Seiten mit demselben Inhalt, indem Sie das Attribut rel="canonical" für die privilegierte Version der Seite angeben. Das Attribut rel="canonical" kann innerhalb einer oder mehrerer Domänen verwendet werden.

Optimieren Sie die Indizierung von paginierten Inhalten (z. B. Seite=1 und Seite=2 aus der Kategorie „Gummibonbons“), indem Sie entweder:

  • Fügen Sie einer Reihe von Seiten ein rel="canonical"-Attribut hinzu, das die kanonische Kategorie mit dem Parameter „view-all“ angibt (z. B. Seite=1, Seite=2 und Seite=3 aus der Kategorie „Gummibonbons“ mit with rel=“canonical“ ein Kategorie=Gummibonbons&Seite=alle), um sicherzustellen, dass die Seite für Benutzer relevant ist und schnell geladen wird.
  • Verwenden Sie die Paginierungsmarkierung rel="next" und rel="prev", um die Beziehung zwischen einzelnen Seiten anzuzeigen (siehe Artikel „Paginierung mit rel="next" und rel="prev" ").
Fügen Sie in Ihre Sitemaps nur kanonische Links ein.

Für jeden Beitrag und WordPress-Beiträge Der Benutzer kann eine oder mehrere Überschriften (Kategorien) angeben. Mit dieser Funktion können Sie Beiträge mit ähnlicher Bedeutung gruppieren und Besuchern die Möglichkeit geben, nur die Abschnitte zu lesen und anzuzeigen, die ihnen gefallen. Als ich beispielsweise meinen Hauptblog, Tod’s Blog, erstellte, wollte ich über alle Nuancen des Internets schreiben – vom Design bis zur Programmierung. Nehmen wir an, eine Person gelangte von einer Suchmaschine zu einem Artikel über Wordpress und wollte noch mehr über das System lesen – sie müsste die Archive durchstöbern, die Suche erneut verwenden oder alle Beiträge hintereinander durchsehen. All dies hätte natürlich vermieden werden können, wenn man sich einer speziellen Kategorie namens WordPress zugewandt hätte. Oder für diejenigen, die sich beispielsweise nur für Design interessieren, könnte ein Blog-Bereich interessant sein.

Schaut man sich den Blog-Header genau an, erkennt man dort eine Art Menü WordPress-Kategorien fungieren als Teil des Projekts. Für mich ist dies eine recht praktische und visuelle Möglichkeit, die Themen der Einträge zu trennen.

Ganz in der Mitte der Seite sehen Sie ein Formular zum Hinzufügen einer neuen Kategorie. Hier müssen Sie den Namen (Name), das Etikett (Teil) angeben Links-URLs für CNC), übergeordnete Kategorie (falls vorhanden) und Sie können auch festlegen kurze Beschreibung. Mit der übergeordneten Kategorie können Sie in WordPress Abschnitte mit mehreren Verschachtelungsebenen erstellen. Für die Kategorie „WordPress“ in einigen IT-Blogs können Sie beispielsweise dieselben Vorlagen, Plugins usw. hinzufügen.

Auf der rechten Seite der Kategorienseite werden alle WordPress-Kategorien angezeigt und können bearbeitet oder gelöscht werden. Um Aktionen auszuführen, bewegen Sie einfach den Mauszeiger über den Namen einer bestimmten Kategorie. Anschließend wird ein kleines Popup-Menü angezeigt.

Beim Bearbeiten wird Ihnen einer der Informationsblöcke angezeigt, in dem Sie eine oder mehrere Kategorien für den Artikel auswählen können. Aktivieren Sie einfach die Kästchen neben den gewünschten Namen.

Hier können Sie neue Kategorien hinzufügen, indem Sie auf den entsprechenden Link klicken. Der einzige Nachteil dieses Mechanismus besteht darin, dass Sie beim Erstellen nur den Namen und die übergeordnete Kategorie angeben können, während Sie zum Festlegen des Beschriftungsfelds zum Abschnitt „Kategorien“ gehen und dort die Informationen bearbeiten müssen.

Darüber hinaus können Sie Kategorien für Blogbeiträge über deren Liste im Menü „Beiträge – Bearbeiten“ bearbeiten. Wenn Sie mit der Maus über eine bestimmte Veröffentlichung fahren, sehen Sie dort einen Link „ Schnellbearbeitung" Klicken Sie darauf und sehen Sie sich das Formular zum Bearbeiten an:

Hier können Sie Kategorien, Tags und alle Zusatzinformationen zum Artikel ändern. Die Sache ist sehr praktisch + es funktioniert ohne Neuladen der Seite.

wp_list_categories-Funktion für WordPress-Kategorie

Traditionell beschäftige ich mich nicht nur mit der Arbeit mit bestimmten Elementen des Systems, sondern stelle auch spezielle Funktionen für Vorlagen bereit. Genau wie ich es besprochen habe. Um also eine Liste von Kategorien mit Links zu ihnen anzuzeigen, verwenden Sie wp_list_categories. Es gibt eine Reihe von Argumenten:

  • show_option_all – zeigt einen Link zu allen Kategorien an, wenn Sie eine Liste als Anzeigestil ausgewählt haben.
  • orderby – Sortieren nach Kategorien nach ID, Name (Name), Label (Slug), Anzahl der Beiträge (Count).
  • order – Sortierreihenfolge (ASC – aufsteigend, DESC – absteigend).
  • show_last_updated – Zeigt das Datum der letzten Aktualisierung an.
  • Stil – Designstil: Liste, Unterteilung durch
    (keiner).
  • show_count – Zeigt die Anzahl der Beiträge in der Kategorie an.
  • hide_empty – leere Kategorien ohne Beiträge ausblenden.
  • use_desc_for_title – Verwenden Sie eine Beschreibung für das Titelattribut im Link.
  • child_of – zeigt nur Kategorien für die angegebene übergeordnete Kategorie an.
  • Feed – zeigt einen Link zum Feed für Kategorien an.
  • feed_type – Feedtyp.
  • feed_image – Bild für das RSS-Symbol.
  • Ausschließen – schließt Kategorien aus der Liste aus und der Parameter child_of wird automatisch deaktiviert.
  • exclude_tree – schließt einen gesamten Kategoriezweig aus.
  • include ist der inverse Parameter, der nur die angegebenen WordPress-Kategorien in die Liste einschließt.
  • hierarchisch – Parameter zur Anzeige von Unterkategorien.
  • title_li – Titel der Kategorienliste.
  • Anzahl – Anzahl der anzuzeigenden Kategorien (wenn es zu viele davon gibt).
  • echo – zeigt Kategorien an, der Standardwert ist „True“.
  • Tiefe – gibt die Anzahl der Ebenen an, in denen Unterkategorien angezeigt werden sollen.

Abschließend werde ich eine Reihe von Beispielen für die Verwendung von wp_list_categories geben. Erstens die Option aus der Kopfzeile dieses Blogs.

„hide_empty=1&exclude=1&title_li=&orderby=count&order=desc&use_desc_for_title=0“) ; ?>

Hier ist es so eingestellt, dass versteckte Kategorien angezeigt werden, Kategorien aus der Liste ausgeschlossen werden, eine leere Zeile für den Blocktitel angezeigt wird und nach der Anzahl der Artikel und in absteigender Reihenfolge sortiert wird (das heißt, ich habe die meisten Artikel im Abschnitt). Das letzte Argument ersetzt nicht die Kategoriebeschreibung im Titel des Links.

Nun, und noch ein paar einfache Situationen. Verwendung von Ausschlüssen und Einschlüssen von Kategorien.

Wenn Sie etwas zu WordPress-Überschriften und -Kategorien hinzufügen möchten, schreiben Sie es in die Kommentare.

Aktualisieren: Möglicherweise benötigen Sie auch einen kleinen Hack, um . In WordPress ist standardmäßig der Texttitel definiert, etwa „Alle Beiträge in der Kategorie anzeigen ...“. Sie können stattdessen einfach den Namen der Kategorie belassen – lesen Sie den Artikel unter dem Link oben.

Gibt ein Array von Objekten zurück, die Informationen zu Kategorien enthalten.

Die an diese Funktion übergebenen Parameter sind den an die Funktion übergebenen Parametern sehr ähnlich wp_list_categories() und kann entweder als Array oder als Abfragezeichenfolge übergeben werden: type=post&order=DESC .

✈ 1 Mal = 0,005625s = sehr langsam| 50000 Mal = 11,98 s = langsam| PHP 7.1.11, WP 4.9.5

Verwendung

$categories = get_categories($args);

Nutzungsmuster

$categories = get_categories(array("taxonomy" => "category", "type" => "post", "child_of" => 0, "parent" => "", "orderby" => "name", " order“ => „ASC“, „hide_empty“ => 1, „hierarchical“ => 1, „exclude“ => „“, „include“ => „“, „number“ => 0, „pad_counts“ => FALSCH, // vollständige Liste Parameter finden Sie in der Funktionsbeschreibung http://wp-kama.ru/function/get_terms)); if($categories)( foreach($categories as $cat)( // Daten im $cat-Objekt // $cat->term_id // $cat->name (Rubric 1) // $cat->slug (rubrika - 1) // $cat->term_group (0) // $cat->term_taxonomy_id (4) // $cat->taxonomy (category) // $cat->description (Beschreibungstext) // $cat-> parent (0) // $cat->count (14) // $cat->object_id (2743) // $cat->cat_ID (4) // $cat->category_count (14) // $cat-> Category_description (Beschreibungstext) // $cat->cat_name (Rubric 1) // $cat->category_nicename (rubrika-1) // $cat->category_parent (0) ) ) Taxonomie (Linie) Der Name der zu verarbeitenden Taxonomie. Hinzugefügt seit Version 3.0.
Standard: „Kategorie“ Typ (Linie)
  • Beitrag – Kategorien für Beiträge (Standard);
  • Link - Linkabschnitte.
    Standard: „posten“
child_of (Linie) Rufen Sie die untergeordneten Kategorien (einschließlich aller Verschachtelungsebenen) der angegebenen Kategorie ab. Der Parameter gibt die ID der übergeordneten Kategorie an (die Kategorie, deren verschachtelte Kategorien Sie anzeigen möchten). Elternteil(Nummer)
Ruft Kategorien ab, deren übergeordnete Kategorie der im Parameter angegebenen ID entspricht. Der Unterschied zu child_of besteht darin, dass eine Verschachtelungsebene angezeigt wird. Standard: "" (Linie)

bestellen

  • Sortieren der empfangenen Daten nach bestimmten Kriterien. Zum Beispiel nach der Anzahl der Beiträge in jeder Kategorie oder nach Kategorienamen. Folgende Kriterien stehen zur Verfügung:
  • ID – nach ID sortieren;
  • Name – nach Name sortieren (Standard);
  • Schnecke – Sortieren nach Alt. Name (Schnecke);
  • count – nach der Anzahl der Einträge in der Kategorie;

term_group – nach Gruppe.

Standard: „Name“ (Linie)

Befehl

  • Die im Parameter „orderby“ angegebene Sortierrichtung:
  • ASC – in der Reihenfolge vom kleinsten zum größten (1, 2, 3; a, b, c); DESC - in umgekehrte Reihenfolge

, vom größten zum kleinsten (3, 2, 1; c, b, a).

Standard: „ASC“ Hide_empty

(logisch)

  • Ob leere Kategorien (ohne Einträge) empfangen werden sollen oder nicht:
  • 1 (wahr) – keine leeren erhalten,

0 (falsch) – leere erhalten.

Standard: wahr Hide_empty Hierarchisch Wenn der Parameter auf eingestellt ist WAHR
0 (falsch) – leere erhalten., dann enthält das Ergebnis leere untergeordnete Kategorien, deren untergeordnete Kategorien (nicht leere) Einträge haben. ausschließen(String/Array)
Ruft Kategorien ab, deren übergeordnete Kategorie der im Parameter angegebenen ID entspricht. Der Unterschied zu child_of besteht darin, dass eine Verschachtelungsebene angezeigt wird. Schließen Sie alle Kategorien aus der Liste aus. Sie müssen Kategorie-IDs durch Kommas getrennt oder in einem Array angeben. Wenn dieser Parameter angegeben ist, wird der Parameter child_of überschrieben. ausschließen enthalten
Ruft Kategorien ab, deren übergeordnete Kategorie der im Parameter angegebenen ID entspricht. Der Unterschied zu child_of besteht darin, dass eine Verschachtelungsebene angezeigt wird. Listen Sie nur die angegebenen Kategorien auf. Sie müssen Kategorie-IDs durch Kommas getrennt oder in einem Array angeben. Elternteil Limit. Die Anzahl der Kategorien, die abgerufen werden. Standardmäßig gibt es keine Einschränkungen – alle Kategorien werden abgerufen. Hide_empty pad_counts
Wenn Sie „true“ übergeben, ist die Zahl, die die Anzahl der Beiträge in übergeordneten Kategorien anzeigt, die Summe ihrer Beiträge und Beiträge aus untergeordneten Kategorien.

Standard: false

Beispiele

#1 Dropdown-Liste

Um eine Dropdown-Liste mit Kategorien zu erstellen, können wir eine andere spezielle Funktion für diesen Zweck verwenden, wp_dropdown_categories() :

Wp_dropdown_categories(array("hide_empty" => 0, "name" => "category_parent", "orderby" => "name", "selected" => $category->parent, "hierarchical" => true, "show_option_none" => __("Keine")));

Allerdings verlieren wir bei diesem Ansatz etwas Flexibilität bei der Erstellung der Liste, da wir eine vollständig erstellte Liste erhalten. Daher ist es in manchen Fällen sinnvoller, mithilfe der Funktion eine Dropdown-Liste zu erstellen get_categories()

"; echo $option; ) ?>

#2 Liste der Kategorien und ihrer Beschreibungen

Dieses Beispiel zeigt uns, wie wir eine Liste von Links zu Kategorien anzeigen können, wobei unmittelbar nach jedem Link eine Beschreibung der Kategorie angezeigt wird (angegeben beim Erstellen/Bearbeiten einer Kategorie):

„Name“, „Bestellung“ => „ASC“)); foreach($categories as $category)( echo " Kategorie:

term_id). "" title="" . sprintf(__("Alle Beiträge in %s anzeigen"), $category->name) . "" " . ">" . $category->name."

"; Echo "

Beschreibung:". $category->description . "

"; Echo "

"; } ?>

Beitragsanzahl: ". $category->count . "

  • Notizen

Siehe: get_terms() Art der Argumente, die geändert werden können.

Liste der Änderungen Ab Version 2.1.0

Eingeführt. Code Kategorien abrufen: wp-includes/category.php

WP 5.3.2 "Kategorie"); $args = wp_parse_args($args, $defaults);", "/** * Filtert die Taxonomie, die zum Abrufen von Begriffen beim Aufruf von get_categories() verwendet wird.")); $args["taxonomy"] = "link_category"; ) $categories = get_terms($args); if (is_wp_error($categories)) ( $categories = array(); ) else ( $categories = (array ) $categories; foreach (array_keys($categories) as $k) ( _make_cat_compat($categories[ $k ]); ) ) return $categories;

Ich machte mich daran, einen Kurs zu schreiben, der die darin dargelegten Ideen umsetzen würde.
Genauer gesagt, da die Schlüsselfunktionalität bereits im Rahmen des Arbeitsframeworks verwendet wurde, begann ich, sie in eine separate Klasse aufzuteilen. Ich möchte diese Gelegenheit nutzen, um den PHPClub-Mitgliedern für ihre Hilfe bei der Korrektur mehrerer kritischer Fehler und nützlichen Kommentare zu danken. Im Folgenden werde ich versuchen, die Hauptfunktionen zu beschreiben, aber zunächst ein wenig

Haftungsausschluss

Es gibt mehrere Möglichkeiten, mit SQL zu arbeiten: Sie können einen Abfrage-Builder verwenden, Sie können ein ORM verwenden, Sie können mit reinem SQL arbeiten. Ich habe mich für die letzte Option entschieden, weil sie mir näher liegt. Ich glaube nicht, dass die ersten beiden überhaupt schlecht sind. Es ist nur so, dass ich persönlich mich in ihrem Rahmen immer eingeengt gefühlt habe. Aber ich behaupte keineswegs, dass meine Version besser ist. Es ist nur eine weitere Option. Was unter anderem beim Schreiben eines ORM verwendet werden kann. Auf jeden Fall glaube ich, dass ein sicherer Umgang mit reinem SQL nicht schaden kann. Aber gleichzeitig könnte es den letzten verbliebenen Anhängern der Verwendung von mysql_* im Anwendungscode helfen, diese schlechte Praxis endgültig aufzugeben.


Kurz gesagt, die Klasse basiert auf einer Reihe von Hilfsfunktionen, die es Ihnen ermöglichen, die meisten Datenbankoperationen in einer Zeile auszuführen und gleichzeitig (im Gegensatz zu Standard-APIs) Folgendes bereitzustellen: voll Schutz vor SQL-Injections, implementiert mithilfe eines erweiterten Satzes von Platzhaltern, die alle Arten von Daten schützen, die Gegenstand einer Anfrage sein können.
Der Unterricht basiert auf drei Grundprinzipien:
  1. 100 % Schutz vor SQL-Injections
  2. Gleichzeitig ist der Schutz sehr komfortabel zu verwenden, da der Code kürzer statt länger wird
  3. Vielseitigkeit, Portabilität und einfache Erlernbarkeit
Ich werde auf jeden einzelnen Punkt etwas näher eingehen.

Sicherheit

wird durch genau die beiden Regeln gewährleistet, die ich im Artikel formuliert habe:
  1. Beliebig- keine Ausnahmen! - Dynamische Elemente sind in der Anfrage enthalten nur durch Platzhalter.
  2. Alles, was nicht durch Platzhalter ersetzbar ist, durchläuft zunächst die Whitelist.
Leider bieten Standardbibliotheken keinen vollständigen Schutz vor Injektionen und schützen nur Zeichenfolgen und Zahlen mithilfe vorbereiteter Anweisungen.
Um den Schutz zu vervollständigen, mussten wir daher das offensichtlich begrenzte Konzept vorbereiteter Aussagen zugunsten eines umfassenderen Konzepts aufgeben – Platzhalter. Darüber hinaus typisierte Platzhalter (das kennen wir alle aus der printf()-Funktionsfamilie: %d ist ein Platzhalter, der dem Parser mitteilt, wie er den ersetzten Wert verarbeiten soll, in diesem Fall als Ganzzahl). Die Innovation erwies sich als so erfolgreich, dass sie viele Probleme auf einmal löste und den Code deutlich vereinfachte. Ich werde weiter unten mehr über getippte Platzhalter schreiben.
Die Unterstützung der Filterung nach Whitelists wird durch zwei etwas weit hergeholte, aber dennoch notwendige Funktionen bereitgestellt.

Bequemlichkeit und Kürze des Anwendungscodes

Auch hier haben mir typisierte Platzhalter sehr geholfen, die es mir ermöglichten, Funktionsaufrufe einzeilig durchzuführen und sowohl die Anfrage als auch die Daten dafür gleichzeitig zu übergeben. Plus eine Reihe von Helfern, die denen in PEAR::DB ähneln – Funktionen, die sofort ein Ergebnis des gewünschten Typs zurückgeben. Alle Helfer sind nach dem gleichen Schema organisiert: Ein obligatorischer Parameter wird an die Funktion übergeben – eine Anfrage mit Platzhaltern, und beliebig viele optionale Parameter, deren Anzahl und Reihenfolge mit der Anzahl und Reihenfolge der Platzhalter in der Anfrage übereinstimmen müssen . Funktionen der Ind-Familie verwenden einen weiteren erforderlichen Parameter – den Namen des Feldes, nach dem das zurückgegebene Array indiziert wird.
Aufgrund meiner Erfahrung bin ich zu folgendem Satz von Rückgabewerten (und damit auch von Helfern) gekommen:
  • query() – gibt die MySQL-Ressource zurück. Kann traditionell mit fetch() usw. verwendet werden.
  • getOne() – gibt einen Skalar zurück, das erste Element der ersten Zeile des Ergebnisses
  • getRow() – eindimensionales Array, die erste Zeile des Ergebnisses
  • getCol() – eindimensionales Array von Skalaren – Tabellenspalte
  • getAll() – zweidimensionales Array, indiziert nach Zahlen der Reihe nach
  • getInd() – ein zweidimensionales Array, das durch die Werte des durch den ersten Parameter angegebenen Feldes indiziert ist
  • getIndCol() ist ein Array von Skalaren, die vom Feld ab dem ersten Parameter indiziert werden. Unverzichtbar für die Zusammenstellung von Wörterbüchern wie key => value
Dadurch werden die meisten Aufrufe der Datenbank auf ein oder zwei Zeilenkonstruktionen reduziert (anstelle von 5–10 beim herkömmlichen Ansatz):
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
Dieser Code enthält nur die notwendigen und sinnvollen Elemente, es gibt jedoch nichts Überflüssiges und Wiederholendes. Alle Details sind ordentlich in der Klasse versteckt: Mit dem Helfer getAll() können Sie sofort das gewünschte Ergebnis erhalten, ohne Schleifen in den Anwendungscode schreiben zu müssen, und typisierte Platzhalter ermöglichen dies sicher Fügen Sie der Anfrage dynamische Elemente hinzu beliebig Typen ohne manuelle Angabe von Bindungen (bind_param). Extra DRY-Code! Bei Verwendung der Platzhalter ?a und ?u wird der Unterschied in der Codemenge sogar noch größer:
$data = $db->getAll("SELECT * FROM table WHERE Category IN (?a)",$ids);

Vielseitigkeit und Leichtigkeit des Lernens

Stehen Sie auf drei Säulen:
  1. Sehr kleine API – ein halbes Dutzend Platzhalter und die gleiche Anzahl an Helfern.
  2. Wir arbeiten mit dem guten alten SQL, das nicht neu erlernt werden muss.
  3. Die scheinbar unauffällige, aber unglaublich nützliche Funktion parse() war ursprünglich nur zum Debuggen gedacht, entwickelte sich aber schließlich zu einem Schlüsselelement beim Schreiben komplexer Abfragen.
Dadurch werden alle komplexen Abfragen auf altmodische Weise – zum Beispiel in einer Schleife – zusammengestellt, aber gleichzeitig unter Einhaltung aller Sicherheitsregeln!
Lassen Sie mich Ihnen ein kleines Beispiel geben (komplexere Beispiele finden Sie in der Dokumentation unter dem Link am Ende des Artikels):
Ein ziemlich häufiger Fall, wenn wir einer Abfrage eine Bedingung hinzufügen müssen, wenn eine Variable vorhanden ist

$sqlpart = ""; if (!empty($var)) ( $sqlpart = $db->parse(" AND field = ?s", $var); ) $data = $db->getAll("SELECT * FROM table WHERE a=? i ?p", $id, $sqlpart);
Hier ist es wichtig, einige Dinge zu beachten.
Da wir erstens nicht an die native API gebunden sind, verbietet uns niemand, nicht die gesamte Anfrage, sondern nur einen Teil davon zu analysieren. Dies erweist sich als äußerst praktisch für Abfragen, die nach einer bestimmten Logik zusammengestellt werden: Wir analysieren nur einen Teil der Anfrage und ersetzen ihn dann durch einen speziellen „Leerlauf“-Platzhalter in die Hauptanfrage, um wiederholtes Parsen zu vermeiden (und die Anforderungen zu erfüllen). mit der Regel „Alle Elemente werden nur durch Platzhalter ersetzt“).
Aber leider ist dies der Schwachpunkt der gesamten Klasse. Im Gegensatz zu allen anderen Platzhaltern (die selbst bei falscher Verwendung nie zu einer Injektion führen) kann die falsche Verwendung des Platzhalters?p dazu führen.
Allerdings würde die Narrensicherheit die Klasse erheblich verkomplizieren, sie würde jedoch immer noch nicht vor dem dummen Einfügen einer Variablen in eine Abfragezeichenfolge schützen. Also habe ich beschlossen, es so zu belassen, wie es ist. Aber wenn Sie einen Weg kennen, dieses Problem ohne allzu viel Overengineering zu lösen, wäre ich für Ideen dankbar.

Am Ende haben wir jedoch einen leistungsstarken und leichten Abfragegenerator erhalten, der diesen kleinen Nachteil mehr als rechtfertigt.
Leistungsstark, weil wir nicht auf die Abfrage-Builder-Syntax „SQL geschrieben in PHP“ beschränkt sind – wir schreiben reines SQL.
Einfach, weil die gesamte Abfrageerstellungs-API aus einem halben Dutzend Platzhaltern und der Funktion parse() besteht
Hier ist mein Lieblingsbeispiel – Einfügen mit MySQL-Funktionen
$data = array("field"=>$value,"field2"=>$value); $sql = "INSERT INTO table SET ts=unix_timestamp(), ip=inet_aton(?s),?u"; $db->query($sql, $ip, $data);
Einerseits bewahren wir die SQL-Syntax, andererseits machen wir sie sicher und drittens reduzieren wir die Codemenge radikal.

Mehr über typisierte Platzhalter

Beantworten wir zunächst die Frage: Warum überhaupt Platzhalter?
Dies ist im Allgemeinen bereits ein alltäglicher Ort, aber ich wiederhole dennoch: Alle dynamischen Daten sollten nur über Platzhalter in die Anfrage einbezogen werden aus folgenden Gründen:
  • Das Wichtigste ist die Sicherheit. Durch das Hinzufügen einer Variablen über einen Platzhalter können wir sicher sein, dass sie korrekt formatiert wird.
  • lokale Formatierung. Dies ist ein ebenso wichtiger Punkt. Erstens werden die Daten unmittelbar vor der Eingabe der Anfrage formatiert und haben keinen Einfluss auf die ursprüngliche Variable, die dann woanders verwendet werden kann. Zweitens werden die Daten genau dort formatiert, wo sie benötigt werden, und nicht vor dem Start des Skripts, wie bei magischen Anführungszeichen, und nicht an zehn möglichen Stellen im Code von mehreren Entwicklern, die sich aufeinander verlassen können.
Wenn wir dieses Konzept weiterentwickeln, kommen wir zu der Idee, dass es Schrittmacher sein müssen getippt. Aber warum?
An dieser Stelle möchte ich einen Moment innehalten und die Entwicklungsgeschichte des Programmierdenkens im Bereich des Injektionsschutzes nachzeichnen.
Zuerst herrschte Chaos – überhaupt kein Schutz, wir haben alles so hingeschoben, wie es war.
Dann wurde es nicht viel besser, mit dem Paradigma „Lasst uns alles vom Benutzer zurückgewinnen, was in das Skript gelangt ist“ und dem Höhepunkt in Form von magischen Zitaten.
Dann kamen die besten Köpfe zu dem Schluss, dass es richtig ist, nicht über Screening, sondern über Formatierung zu sprechen. Denn bei der Formatierung kommt es nicht immer nur auf ein Layout an. So erschien die Methode quote() in PDO, die die Formatierung einer Zeichenfolge vollständig durchführte – nicht nur Sonderzeichen darin maskierte, sondern sie auch in Anführungszeichen setzte, ohne auf den Programmierer angewiesen zu sein. Selbst wenn der Programmierer diese Funktion an der falschen Stelle verwendet hat (z. B. für eine Zahl), funktionierte die Injektion daher immer noch nicht (und im Fall des bloßen Escapens durch mysql_real_escape_string geht sie problemlos durch, wenn wir eine Zahl eingeben die Abfrage, ohne sie in Anführungszeichen zu setzen). Bei der Formatierung eines Bezeichners führte diese Funktion in der Entwicklungsphase zu einem Fehler, der für den Autor des Codes darauf hindeutete, dass er sich ein wenig geirrt hatte.
Leider haben die Autoren von PDO hier aufgehört, da die Idee, dass in einer Abfrage nur Zeichenfolgen formatiert werden müssen, immer noch fest in den Köpfen der Entwickler verankert ist. Aber in Wirklichkeit enthält die Anfrage noch viel mehr Elemente unterschiedlicher Art. Und jedes erfordert seine eigene Formatierung! Das heißt, die einzige quote()-Methode wird uns in keiner Weise passen – wir brauchen viele verschiedene Anführungszeichen. Und das nicht als Ausnahme, „hier ist quoteName()“, sondern als eines der Hauptkonzepte: Jeder Typ hat sein eigenes Format. Nun, da es viele Arten der Formatierung gibt, muss der Typ irgendwie angegeben werden. Und dafür eignet sich am besten ein typisierter Platzhalter.

Darüber hinaus ist ein getippter Platzhalter SEHR praktisch!
Erstens, weil ein spezieller Operator zum Binden eines Werts an einen Platzhalter unnötig wird (Sie können aber trotzdem den Typ des übergebenen Werts angeben!)
Zweitens können wir, da wir einen typisierten Platzhalter erfunden haben, eine große Anzahl dieser Platzhalter einfügen, um viele Routineaufgaben beim Schreiben von SQL-Abfragen zu lösen.
Zunächst werden wir einen Platzhalter für Bezeichner erstellen – wir vermissen ihn im wirklichen Leben dringend und nicht in der Fantasie der Autoren von Standard-APIs. Sobald der Entwickler vor der Notwendigkeit steht, der Anfrage dynamisch einen Feldnamen hinzuzufügen, beginnt jeder auf seine Weise zu pervertieren, manche in den Wald, manche in Brennholz. Hier ist alles mit den übrigen Anforderungselementen vereinheitlicht, und das Hinzufügen einer Kennung ist nicht schwieriger als das Hinzufügen einer Zeichenfolge. Gleichzeitig wird der Bezeichner jedoch nicht als Zeichenfolge formatiert, sondern nach seinen eigenen Regeln – er wird in Anführungszeichen gesetzt und innerhalb dieser Anführungszeichen wird durch Verdoppelung maskiert.
Außerdem. Das nächste Problem für jeden Entwickler, der jemals versucht hat, vorbereitete Standardanweisungen im wirklichen Leben zu verwenden, ist der IN()-Operator. Voila, wir haben auch einen Platzhalter für diesen Vorgang! Die Array-Ersetzung wird nicht schwieriger als bei allen anderen Elementen vereinheitlicht bei ihnen gibt es keine separaten Funktionen, lediglich der Buchstabe im Platzhalter ändert sich.
Genauso erstellen wir den Platzhalter für SET. Ich kann nicht widerstehen, zu zeigen, wie einfach der Code für eine so verwirrende Abfrage wie INSERT... ON DUPLICATE wird:
$data = array("offers_in" => $in, "offers_out" => $out); $sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u"; $db->query($sql,$pid,$data,$data);
Derzeit unterstützt die Klasse 6 Arten von Platzhaltern

  • ?s („string“) – Zeichenfolgen (sowie DATE, FLOAT und DECIMAL).
  • ?i („Ganzzahl“) – ganze Zahlen.
  • ?n („Name“) – Namen von Feldern und Tabellen
  • ?p („parsed“) – um bereits verarbeitete Teile der Anfrage einzufügen
  • ?a („Array“) – eine Reihe von Werten für IN (Zeichenfolge wie „a“, „b“, „c“)
  • ?u („update“) – eine Reihe von Werten für SET (Zeichenfolge wie `field`="value", `field`="value")
Was für meine Aufgaben völlig ausreicht, dieser Satz lässt sich aber jederzeit mit beliebigen anderen Platzhaltern erweitern, zum Beispiel für Bruchzahlen. Ich sehe keinen Sinn darin, einen separaten Platzhalter für NULL zu erstellen – er kann immer direkt in die Anfrage eingefügt werden.
Ich habe beschlossen, PHP NULL nicht automatisch in SQL NULL zu übersetzen. Möglicherweise wird der Code dadurch etwas komplizierter (in den seltenen Fällen, in denen dies erforderlich ist), aber es verringert seine Mehrdeutigkeit.

Wie viele vielleicht bemerkt haben, erinnert diese Klasse übrigens in vielerlei Hinsicht an die DbSimple-Bibliothek von Dmitry Koterov. Aber ich habe grundlegende Meinungsverschiedenheiten mit einigen der darin enthaltenen Ideen.
Erstens bin ich ein Gegner jeglicher Magie, wenn dieselbe Funktion je nach Art der übergebenen Daten unterschiedliche Ergebnisse zurückgeben kann. Das macht das Schreiben vielleicht etwas einfacher, macht aber auch die Wartung und das Debuggen des Codes furchtbar schwierig. Deshalb wird in meiner Klasse jegliche Magie auf ein Minimum beschränkt und alle Operationen und Datentypen immer explizit geschrieben.
Zweitens hat DbSimple meiner Meinung nach eine etwas zu komplizierte Syntax. Einerseits sind geschweifte Klammern eine geniale Idee. Warum sollten wir andererseits auch das tun, wenn uns die ganze Leistungsfähigkeit von PHP zur Verfügung steht? Deshalb habe ich mich entschieden, den anderen Weg zu gehen und „externe“ Logik einzuführen, die nur durch die PHP-Syntax begrenzt ist, anstelle der „internen“ – offensichtlich begrenzten – Logik. Die Hauptsache ist, dass alle dynamischen Elemente nur über Platzhalter in die Anfrage gelangen, und der Rest hängt nur von der Vorstellungskraft des Entwicklers (und der Funktion parse()) ab.

Der Klassencode ist auf Github verfügbar, github.com/colshrapnel/safemysql/blob/master/safemysql.class.php
Spickzettel mit grundlegenden Befehlen und Beispielen: phpfaq.ru/misc/safemysql_cheatsheet_ru.pdf
Eine gute Vorstellung von den Möglichkeiten erhalten Sie auf der Dokumentationsbeispielseite (leider noch nicht fertig), phpfaq.ru/safemysql
Es gibt auch Antworten auf häufig gestellte Fragen, wie zum Beispiel „Warum verwenden Sie nicht muttersprachliche vorbereitete Anweisungen?“ usw.
Ich beantworte jedoch gerne alle Fragen in den Kommentaren und verbessere sowohl den Kurs selbst als auch diesen Artikel basierend auf Ihren Kommentaren.