Nesneye yönelik dillerde, nesneler ne zaman kendi başlarına işlem yapmalı ve nesneler üzerinde ne zaman işlem yapılmalıdır?


11

PageSayfa oluşturucuya yönelik bir dizi talimatı temsil eden bir sınıf olduğunu varsayalım . Ve Rendererbir sayfanın ekranda nasıl oluşturulacağını bilen bir sınıf olduğunu varsayalım . Kodu iki farklı şekilde yapılandırmak mümkündür:

/*
 * 1) Page Uses Renderer internally,
 * or receives it explicitly
 */
$page->renderMe(); 
$page->renderMe($renderer); 

/*
 * 2) Page is passed to Renderer
 */
$renderer->renderPage($page);

Her yaklaşımın artıları ve eksileri nelerdir? Ne zaman daha iyi olacak? Diğeri ne zaman daha iyi olacak?


ARKA FON

Biraz daha arka plan eklemek için - kendimi aynı kodda her iki yaklaşımı kullanarak buluyorum. Adlı üçüncü taraf PDF kitaplığı kullanıyorum TCPDF. Kodumda bir yerde PDF oluşturma çalışması için aşağıdakilere sahip olmak zorunda:

$pdf = new TCPDF();
$html = "some text";
$pdf->writeHTML($html);

Sayfanın bir temsilini oluşturmak istediğimi varsayalım. Bir PDF sayfası snippet'ini oluşturmak için talimatları tutan bir şablon oluşturabilirim:

/*
 * A representation of the PDF page snippet:
 * a template directing how to render a specific PDF page snippet
 */
class PageSnippet
{    
    function runTemplate(TCPDF $pdf, array $data = null): void
    {
        $pdf->writeHTML($data['html']);
    }
}

/* To be used like so */
$pdf = new TCPDF();
$data['html'] = "some text";
$snippet = new PageSnippet();
$snippet->runTemplate($pdf, $data);

1) Burada, ilk kod örneğimde olduğu gibi, $snippet kendini çalıştıran uyarı . Ayrıca çalışabilmesi için $pdfve her şeyi bilmeli ve bunlara aşina olmalıdır $data.

Ancak, PdfRendererböyle bir sınıf oluşturabilirim :

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf)
    {
        $this->pdf = $pdf;
    }

    function runTemplate(PageSnippet $template, array $data = null): void
    {
        $template->runTemplate($this->pdf, $data);
    }
}

ve sonra kodum bu döner:

$renderer = new PdfRenderer(new TCPDF());
$renderer->runTemplate(new PageSnippet(), array('html' => 'some text'));

2) Burada çalışması için gerekli $rendererolan PageSnippetve alır $data. Bu ikinci kod örneğime benzer.

Dolayısıyla, oluşturucu sayfa snippet'ini alsa bile, oluşturucunun içinde snippet yine de kendi kendine çalışır . Yani her iki yaklaşım da oyunda. OO kullanımınızı yalnızca bir tanesiyle veya yalnızca diğeriyle sınırlandırabileceğinizden emin değilim. Birbirini maskeleseniz bile her ikisi de gerekli olabilir.


2
Ne yazık ki, burada yazılım "dini savaşları" dünyasına, stil kullanmak için ayracı, vb kullanın boşluk veya sekme kullanıp kullanma satırları boyunca dolaştınız. Burada "daha iyi", her iki tarafta sadece güçlü görüşler yoktur. Hem zengin hem de anemik etki alanı modellerinin faydalarını ve dezavantajlarını internette araştırın ve kendi fikrinizi oluşturun.
David Arno

7
@DavidArno Dinlediğiniz alanları kullanın ! :)
candied_orange

1
Ha, bu siteyi zaman zaman ciddi olarak anlamıyorum. İyi cevaplar alan çok iyi sorular hiçbir zaman görüşe dayalı değildir. Yine de bunun gibi açıkça görüşe dayalı bir soru ortaya çıkıyor ve olağan şüpheliler hiçbir yerde bulunamıyor. Oh, eğer onları ve hepsini yenemezsen ... :)
David Arno

@Erik Eidt, çok iyi bir "ileri seçenek" yanıtı olarak hissettiğim için lütfen yanıtınızı geri alabilir misiniz?
David Arno

1
SOLID prensiplerinin yanı sıra , özellikle Uzman kısmında GRASP'a göz atabilirsiniz . Soru, sorumluluğu yerine getirmeniz için hangi bilgilere sahip olduğudur.
OnesimusUnbound

Yanıtlar:


13

Bu tamamen OO'nun ne düşündüğüne bağlıdır .

OOP = SOLID için, sınıfın Tek Sorumluluğunun bir parçasıysa, işlem sınıfın bir parçası olmalıdır.

OO = sanal gönderme / polimorfizm için, dinamik olarak gönderilmesi gerekiyorsa, yani bir arabirim üzerinden çağrıldığında işlem nesnenin bir parçası olmalıdır.

OO = kapsülleme için, ifşa etmek istemediğiniz dahili durumu kullanıyorsa, işlem sınıfın bir parçası olmalıdır.

OO = “Akıcı arayüzleri seviyorum” sorusu, hangi varyantın daha doğal okuduğu sorusudur.

OO = gerçek dünya varlıklarını modellemek için, hangi gerçek dünya varlığı bu işlemi gerçekleştirir?


Tüm bu bakış açıları yalnız başına yanlıştır. Ancak bazen bu perspektiflerden biri veya daha fazlası bir tasarım kararına varmada yardımcı olur.

Örneğin polimorfizm bakış açısını kullanma: Farklı oluşturma stratejileriniz (farklı çıktı biçimleri veya farklı oluşturma motorları gibi) varsa, $renderer->render($page)çok mantıklıdır. Ancak, farklı şekilde işlenmesi gereken farklı sayfa türleriniz varsa $page->render()daha iyi olabilir. Çıktı hem sayfa türüne hem de oluşturma stratejisine bağlıysa, ziyaretçi kalıbı üzerinden iki kez gönderim yapabilirsiniz.

Birçok dilde, işlevlerin yöntem olması gerekmediğini unutmayın. render($page)Genellikle mükemmel bir ince (ve olağanüstü basit) bir çözüm gibi basit bir işlev .


Bir dakika bekle. Sayfa oluşturucuya bir referans içeriyorsa, ancak hangi oluşturucuyu tuttuğunu bilmiyorsa polimorfik render elde edebilirim. Bu, polimorfizmin tavşan deliğinden biraz daha aşağıda olduğu anlamına gelir. Ayrıca oluşturucuya ne geçeceğini seçip seçebilirim. Tüm sayfayı geçmek zorunda değilim.
candied_orange

@CandiedOrange Bu iyi bir nokta, ancak SRP altında argümanınızı ayırtacağım: belki de bir çeşit polimorfik renderleme stratejisi kullanarak nasıl oluşturulacağına karar vermek sayfanın sermaye-R Sorumluluğu olurdu.
amon

$rendererNasıl render edeceğine karar vereceğini düşündüm . Ne zaman söyleyeceklerini $pagekonuştuğunda $rendererne render eder. Nasıl değil. $pageNasıl hiçbir fikri yok. Bu beni SRP sorununa mı sokuyor?
candied_orange

Gerçekten aynı fikirde olmadığımızı sanmıyorum. İlk yorumunuzu bu cevabın kavramsal çerçevesine sıralamaya çalışıyordum, ama beceriksiz kelimeler kullanmış olabilirim. Bana cevapta bahsetmediğimi hatırlattığınız bir şey var: sorma-sorma veri akışı da iyi bir buluşsal yöntemdir.
amon

Hmm tamam. Haklısın. Bahsettiğim şey söyleme-sorma peşindeydi. Şimdi yanılıyorsam beni düzeltin. Oluşturucunun bir sayfa referansı aldığı diğer strateji, oluşturucunun sayfa alıcılarını kullanarak geri dönüp sayfa için bir şeyler istemesi gerektiği anlamına gelir.
candied_orange

2

Alan Kay'a göre nesneler kendi kendine yeterli, "yetişkin" ve sorumlu organizmalardır. Yetişkinler bir şeyler yaparlar, ameliyat edilmezler. Yani, finansal işlem kendini kurtarmaktan sorumludur , sayfa kendini oluşturmaktan vb. Sorumludur. Daha doğrusu, kapsülleme OOP'ta büyük şeydir. Özellikle, ünlü Tell sorma ilkesi (@CandiedOrange her zaman bahsetmeyi sever :)) ve alıcıların ve ayarlayıcıların kamuoyunda soyulmasıyla kendini gösterir .

Uygulamada, veritabanı tesisleri, oluşturma tesisleri vb. Gibi işlerini yapmak için gerekli tüm kaynaklara sahip nesnelerle sonuçlanır.

Örneğiniz göz önüne alındığında, OOP sürümüm aşağıdaki gibi görünecektir:

class Page
{
    private $data;
    private $renderer;

    public function __construct(ICanRender $renderer, $data)
    {
        $this->renderer = $renderer;
        $this->data = $data;
    }

    public function render()
    {
        $this->renderer->render($this->data);
    }
}

İlgilenmeniz durumunda David West, Object Thinking adlı kitabındaki orijinal OOP ilkelerini anlatıyor .


1
Açıkça söylemek gerekirse, 15 yıl önce, tarihsel ilgi dışında, birisinin yazılım geliştirme ile ilgili bir şey hakkında söylediklerini kimin umurunda?
David Arno

1
Nesneye yönelik kavramı icat eden bir adamın hangi nesnenin ne olduğu hakkında ne söylediğine önem veriyorum. ” Neden? Argümanlarınızda "otoriteye başvurma" yanlışlarını kullanmanıza engel olmanın ötesinde, bir mucitin düşüncelerinin, 15 yıl sonraki dönemin başvurusu üzerine ne gibi bir etkisi olabilir?
David Arno

2
@Zapadlo: İletinin neden Sayfadan Renderer'a olduğunu ve bunun tersi olmadığını gösteren bir argüman sunmuyorsunuz. İkisi de nesne ve dolayısıyla her iki yetişkin de değil mi?
JacquesB

1
" Otorite yanlışlığına itiraz burada uygulanamaz " ... " Bu yüzden sizin görüşünüze göre OOP'yi temsil eden kavramlar kümesi aslında yanlıştır (çünkü orijinal tanımın bozulmasıdır "). Bence otorite yanlışlığına itirazın ne olduğunu bilmiyor musunuz? İpucu: burada bir tane kullandınız. :)
David Arno

1
@David Arno Peki, tüm otorite başvuruları yanlış mı? "Görüşüme itiraz etmeyi" tercih eder misiniz? Birisi Bobizm Amca'ya her atıfta bulunduğunda, otoriteye itirazdan şikayet eder misiniz? Zapadio saygın bir kaynak sağladı.Çatışmayan kaynaklara katılmıyorum ya da alıntı yapabilir, ancak birisinin atıfta bulunmadığından şikayet ederek tekrar tekrar yapıcı değil
user949300

2

$page->renderMe();

Burada pagebizzat kendini yaratmaktan tamamen sorumluyuz. Bir kurucu aracılığıyla bir render sağlanmış olabilir veya bu işlevselliği yerleşik olarak içerebilir.

Parametre olarak geçirmeye oldukça benzer olduğu için, ilk örneği (bir oluşturucu aracılığıyla bir render ile birlikte verilir) görmezden geleceğim. Bunun yerine yerleşik işlevselliğin artılarını ve eksilerini inceleyeceğim.

Pro, çok yüksek bir kapsülleme seviyesine izin vermesidir. Sayfanın iç durumu hakkında doğrudan bir şey açıklamasına gerek yok. Sadece kendini göstererek ortaya koyar.

Con, tek sorumluluk ilkesini (SRP) ihlal etmesidir. Bir sayfanın durumunu kapsüllemekten sorumlu bir sınıfımız var ve aynı zamanda kendini nasıl oluşturacağına dair kurallarla kodlanmış ve bu nedenle nesnelerin "kendilerine bir şeyler yapması, başkaları tarafından bir şeyler yapmaması gerekeceği için büyük olasılıkla bir dizi diğer sorumluluklar var. ".

$page->renderMe($renderer);

Burada hâlâ bir sayfanın kendisini işleyebilmesini istiyoruz, ancak gerçek oluşturmayı gerçekleştirebilecek yardımcı bir nesne sağlıyoruz. Burada iki senaryo ortaya çıkabilir:

  1. Sayfanın, bu oluşturmayı oluşturmak için oluşturma kurallarını (hangi sırayla çağırmak için hangi yöntemleri çağırması) bilmesi gerekir. Kapsülleme korunur, ancak sayfanın hâlâ oluşturma işlemini denetlemesi gerektiğinden SRP hala bozuktur veya
  2. Sayfa, oluşturucu nesnesinde yalnızca bir yöntem çağırıyor ve ayrıntılarını içeri aktarıyor. SRP'ye saygı duymaya yaklaşıyoruz, ancak şimdi kapsüllemeyi zayıflattık.

$renderer->renderPage($page);

Burada, SRP'ye tamamen saygı duyduk. Sayfa nesnesi bir sayfada bilgi bulundurmaktan ve oluşturucu bu sayfayı oluşturmaktan sorumludur. Ancak, sayfa nesnesinin kapsüllenmesini tüm durumunu herkese açık hale getirmesi gerektiği için tamamen zayıflattık.

Ayrıca, yeni bir sorun oluşturduk: oluşturucu artık sayfa sınıfına sıkı sıkıya bağlı. Bir sayfadan farklı bir şey oluşturmak istediğimizde ne olur?

Hangisi en iyi? Hiçbiri. Hepsinin kusurları var.


V3'ün SRP'ye saygı duyduğuna katılmıyorum. Oluşturucunun değiştirmek için en az 2 nedeni vardır: Sayfa değişirse veya oluşturma şekliniz değişirse. Renderer'ın Sayfalar dışında nesneler oluşturması gerekiyorsa, bunlardan üçüncüsü kapsar. Aksi takdirde, güzel analiz.
user949300

2

Bu sorunun cevabı açıktır. Öyle $renderer->renderPage($page);doğru bir uygulama olduğu. Bu sonuca nasıl ulaştığımızı anlamak için kapsüllemeyi anlamamız gerekiyor.

Sayfa nedir? Birinin tüketeceği bir ekranın temsilidir. "Birisi" insan veya bot olabilir. Not Pagetemsilidir değil, ekran kendisidir. Temsil edilmeden bir temsil var mı? Sayfa oluşturucu içermeyen bir şey mi? Cevap Evet, bir temsil temsil edilmeden var olabilir. Temsil etmek daha sonraki bir aşamadır.

Sayfa içermeyen oluşturucu nedir? Bir oluşturucu sayfa olmadan görüntülenebilir mi? Hayır. Bu yüzden bir Renderer arayüzü renderPage($page);yönteme ihtiyaç duyar .

Sorun nedir $page->renderMe($renderer);?

renderMe($renderer)İçsel olarak hala aramak zorunda kalacak gerçektir $renderer->renderPage($page);. Bu ihlal Demeter Kanunu devletler

Her birimin diğer birimler hakkında sınırlı bilgiye sahip olması gerekir

PageSınıf var olup olmadığını umursamıyor Rendererevrende. Yalnızca bir sayfanın temsili olmakla ilgilenir. Bu nedenle sınıf veya arabirim Rendererasla a Page.


GÜNCEL YANIT

Sorunuzu doğru bulduysam, PageSnippetsınıf yalnızca bir sayfa pasajı olmakla ilgilenmelidir.

class PageSnippet
{    
    /** string */
    private $html;

    function __construct($data = ['html' => '']): void
    {
        $this->html = $data['html'];
    }

   public function getHtml()
   {
       return $this->html;
   }
}

PdfRenderer renderleme ile ilgilidir.

class PdfRenderer
{
    /**@var TCPDF */
    protected $pdf;

    function __construct(TCPDF $pdf = new TCPDF())
    {
        $this->pdf = $pdf;
    }

    function runTemplate(string $html): void
    {
        $this->pdf->writeHTML($html);
    }
}

İstemci kullanımı

$renderer = new PdfRenderer();
$snippet = new PageSnippet(['html' => '<html />']);
$renderer->runTemplate($snippet->getHtml());

Dikkate alınması gereken birkaç nokta:

  • İlişkisel $databir dizi olarak geçmesi kötü bir uygulamadır . Bir sınıf örneği olmalıdır.
  • Sayfa biçiminin dizinin htmlözelliği içinde yer $dataalması, alan adınıza özgü bir ayrıntıdır ve PageSnippetbu ayrıntıların farkındadır.

Ancak, Sayfalara ek olarak Resimler, Makaleler ve Triptich'leriniz varsa ne olur? Programınızda, bir Renderer hepsini bilmek zorunda kalacak. Bu çok fazla sızıntı. Düşünce için sadece yiyecek.
user949300

@ user949300: Eğer Renderer'ın resim vb. işleyebilmesi gerekiyorsa, o zaman açıkçası onlar hakkında bilmek gerekir.
JacquesB

1
Kent Beck'in Smalltalk Best Practice Patterns , her ikisinin de desteklendiği Reversing Method modelini sunar. Bağlantılı makale, bir nesnenin bir printOn:aStreamyöntemi desteklediğini gösterir , ancak tek yaptığı, akışa nesneyi yazdırmasını söylemektir. Cevabınızla benzerlik, hem bir oluşturucuya oluşturulabilen bir sayfanın hem de bir uygulama ve uygun arabirimler arasından seçim yapabileceğiniz bir sayfayı işleyebilecek bir oluşturucuya sahip olmamanızın bir nedeni olmamasıdır.
Graham Lee

2
Her durumda SRP'yi kırmak / fudge etmek zorunda kalacaksınız, ancak Renderer'ın birçok farklı şeyi nasıl oluşturacağını bilmesi gerekiyorsa, bu gerçekten "çok fazla sorumluluktur" ve mümkünse kaçınılmalıdır.
user949300

1
Cevabınızı seviyorum, ancak Page$ renderer'ın farkında olmanın imkansız olduğunu düşünmeye cazipim. Soruma bazı kodlar ekledim, PageSnippetsınıfa bakın . Etkili bir sayfadır, ancak $pdfaslında bu durumda 3. taraf PDF oluşturucu olan bir tür referans vermeden var olamaz . .. Ancak, sanırım ben PageSnippetsadece PDF metin talimatları bir dizi tutan ve başka bir sınıf bu talimatları yorumlamak böyle bir sınıf oluşturmak olabilir . Ben enjekte olmasını önleyebilirsiniz $pdfiçine PageSnippetekstra karmaşıklığı pahasına,
Dennis

1

İdeal olarak, karmaşıklığı azalttığı için sınıflar arasında mümkün olduğunca az bağımlılık istersiniz. Bir sınıf yalnızca gerçekten ihtiyaç duyuyorsa başka bir sınıfa bağımlı olmalıdır.

Sen devlet Page"Bir sayfa oluşturucuya bir dizi talimat" içerir. Böyle bir şey hayal ediyorum:

renderer.renderLine(x, y, w, h, Color.Black)
renderer.renderText(a, b, Font.Helvetica, Color.Black, "bla bla...")
etc...

Bu nedenle $page->renderMe($renderer), Sayfa oluşturucu için bir referansa ihtiyaç duyduğundan öyledir .

Ancak alternatif olarak oluşturma talimatları, doğrudan çağrılardan ziyade bir veri yapısı olarak da ifade edilebilir.

[
  Line(x, y, w, h, Color.Black), 
  Text(a, b, Font.Helvetica, Color.Black, "bla bla...")
]

Bu durumda asıl İşleyici bu veri yapısını Sayfadan alır ve ilgili oluşturma talimatlarını uygulayarak işler. Böyle bir yaklaşımla bağımlılıklar tersine çevrilir - Sayfanın Oluşturucu hakkında bilgi sahibi olması gerekmez, ancak Oluşturucu'ya daha sonra oluşturabileceği bir Sayfa sağlanmalıdır. İkinci seçenek:$renderer->renderPage($page);

Hangisi daha iyi? İlk yaklaşım muhtemelen en basit olanıdır, ikincisi ise çok daha esnek ve güçlüdür, bu yüzden sanırım gereksinimlerinize bağlıdır.

Karar veremiyorsanız veya gelecekte yaklaşımı değiştirebileceğinizi düşünüyorsanız, bir dolaylı yolun, bir fonksiyonun arkasındaki kararı gizleyebilirsiniz:

renderPage($page, $renderer)

Tavsiye etmeyeceğim tek yaklaşım $page->renderMe(), bir sayfanın yalnızca tek bir oluşturucuya sahip olabileceğini düşündürmesi. Peki ya bir ScreenRendererekleyip eklerseniz PrintRenderer? Aynı sayfa her ikisi tarafından da oluşturulabilir.


EPUB veya HTML bağlamında, sayfa kavramı oluşturucu olmadan mevcut değildir.
mouviciel

1
@mouviciel: Ne demek istediğini anladığımdan emin değilim. Şüphesiz bir HTML sayfanızın oluşturulmasını sağlayabilirsiniz. Örneğin, Google tarayıcı süreç sayfalarını oluşturmadan.
JacquesB

2
Kelime sayfasının farklı bir fikri vardır: yazdırılacak şekilde biçimlendirilmiş bir HTML sayfası olduğunda sayfalandırma işleminin sonucu, belki de @mouviciel'in aklında olan şey budur. Bununla birlikte, bu soruda a page, açıkça bu fikre uymayan bir çıktıya değil, oluşturucuya bir girdidir.
Doc Brown

1

KATI D parçası diyor

"Soyutlamalar detaylara, detaylar soyutlamalara bağlı olmalıdır."

Peki, sabit bir soyutlama olasılığı daha yüksek, değişme olasılığı daha düşük, muhtemelen bir arabirimi temsil eden Page ve Renderer arasında? Aksine, "detay" hangisi?

Deneyimlerime göre, soyutlama genellikle Renderer'dir. Örneğin, çok soyut ve kararlı, basit bir Akış veya XML olabilir. Ya da oldukça standart bir düzen. Sayfanızın özel bir işletme nesnesi, bir "ayrıntı" olma olasılığı daha yüksektir. Ve "resimler", "raporlar", "grafikler" vb. Gibi işlenecek başka iş nesneleriniz de var.

Ama açıkça tasarımınıza bağlı. Sayfa, örneğin <article>standart alt bölümleri olan bir HTML etiketinin eşdeğeri olabilir . Ve birçok farklı özel iş raporlama "renderer" var. Bu durumda, Oluşturucu Sayfaya bağlı olmalıdır.


0

Sınıfların çoğunun iki kategoriden birine ayrılabileceğini düşünüyorum:

  • Veri içeren sınıflar (değişebilir veya değişmez önemli değil)

Bunlar, başka hiçbir şeye neredeyse hiç bağımlılığı olmayan sınıflardır. Bunlar genellikle alan adınızın bir parçasıdır. Hiçbir mantık içermemeli veya sadece doğrudan durumundan türetilebilen mantık içermemelidir. Bir Çalışan sınıfı, isAdultdoğrudan kendi sınıfından türetilebildiği gibi bir işleve sahip olabilir, birthDateancak hasBirthDayharici bilgi gerektiren bir işleve sahip değildir (geçerli tarih).

  • Hizmet veren sınıflar

Bu tür sınıflar veri içeren diğer sınıflarda çalışır. Genellikle bir kez yapılandırılır ve değiştirilemezler (böylece her zaman aynı tür işlevleri yerine getirirler). Bununla birlikte, bu tür sınıflar, bir durumun kısa bir süre (Builder sınıfları gibi) korunmasını gerektiren daha karmaşık işlemleri yapmak için hala durum bilgisi olan kısa ömürlü bir yardımcı örnek sağlayabilir.

Örneğiniz

Örneğin, Pageveri içeren bir sınıf olacaktır. Bu verileri almak ve sınıfın değişebilir olması gerekiyorsa, belki de değiştirmek için işlevleri olmalıdır. Aptal tutun, böylece çok fazla bağımlılık olmadan kullanılabilir.

Veriler veya bu durumda Pageçok çeşitli şekillerde temsil edilebilirsiniz. Web sayfası olarak işlenebilir, diske yazılır, veritabanında depolanır, JSON'a dönüştürülür. Bu vakaların her biri için böyle bir sınıfa yöntem eklemek istemezsiniz (ve sınıfınızın yalnızca veri içermesi gerekiyorsa bile, diğer tüm sınıflara bağımlılıklar oluşturmak).

Sizin Renderertipik bir hizmet türü sınıftır. Belirli bir veri kümesinde çalışabilir ve bir sonuç döndürebilir. Kendine ait çok bir durumu yoktur ve sahip olduğu durum genellikle değişmezdir, bir kez yapılandırılabilir ve sonra tekrar kullanılabilir.

Örneğin , sınıfın a MobileRendererve a StandardRendereruygulamalarının her ikisini birden uygulayabilirsiniz, Rendererancak farklı ayarlarla yapabilirsiniz.

Yani olarak Pageverileri içerir ve dilsiz tutulmalıdır, bu durumda en temiz çözüm geçmek olacaktır Pagea Renderer:

$renderer->renderPage($page)

2
Çok prosedürel mantık.
user949300
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.