Apa metode terbaik untuk menggabungkan dua objek PHP?

Kami memiliki dua objek PHP5 dan ingin menggabungkan konten satu objek ke objek kedua. Tidak ada gagasan tentang subkelas di antara keduanya sehingga solusi yang dijelaskan dalam topik berikut tidak dapat diterapkan.

Bagaimana cara menyalin objek PHP ke jenis objek yang berbeda

//We have this:
$objectA->a;
$objectA->b;
$objectB->c;
$objectB->d;

//We want the easiest way to get:
$objectC->a;
$objectC->b;
$objectC->c;
$objectC->d;

Keterangan:

  • Ini adalah objek, bukan kelas.
  • Objek berisi cukup banyak bidang sehingga foreach akan menjadi sangat lambat.
  • Sejauh ini kami mempertimbangkan untuk mengubah objek A dan B menjadi array kemudian menggabungkannya menggunakan array_merge() sebelum mentransformasikannya kembali menjadi objek tetapi kami tidak bisa mengatakan kami bangga jika ini.

person Veynom    schedule 18.01.2009    source sumber
comment
Objek berisi cukup banyak bidang sehingga proses pendahuluan akan menjadi sangat lambat. - Komputer cukup cepat, 'cukup lambat' sering kali cukup cepat.   -  person Sean McSomething    schedule 28.04.2009


Jawaban (12)


Jika objek Anda hanya berisi bidang (tidak ada metode), ini berfungsi:

$obj_merged = (object) array_merge((array) $obj1, (array) $obj2);

Ini sebenarnya juga berfungsi ketika objek memiliki metode. (diuji dengan PHP 5.3 dan 5.6)

person flochtililoch    schedule 23.12.2009
comment
Anda juga dapat menggunakan array_merge_recursive untuk memiliki perilaku penyalinan yang mendalam. Anda mungkin juga tertarik dengan array_replace_recursive. Perbedaannya dijelaskan secara rinci di sini: brian.serveblog.net /2011/07/31/php-array_replace-vs-array_merge - person Vincent Pazeller; 27.11.2013
comment
Objek yang dihasilkan dari ini akan menjadi turunan dari stdclass. Meskipun ia bekerja pada objek dengan metode, ia secara efektif menghancurkan objek dalam kasus tersebut (dengan menghapus metode). - person Brilliand; 05.02.2015
comment
Ini berguna untuk mengembalikan beberapa kumpulan hasil dalam satu fungsi (Dan hanya mengembalikan objek dengan pasangan nilai kunci.) - person Leonel Atencio; 26.02.2016
comment
Untuk mengonfirmasi komentar @Brilliand, ini tidak berfungsi dengan objek Laravel dalam koleksi Laravel - person brianlmerritt; 22.08.2017
comment
Menggunakan ini untuk menggabungkan objek stdClass yang dikembalikan dari panggilan ke json_decode(). Sederhana dan berhasil. Terima kasih banyak! - person Jamie Carl; 05.12.2017
comment
Ini tidak akan berfungsi jika ada kunci integer di objek. Perhatikan contoh berikut : $arr1 = array('a' =› 9, 'b' =› 'asd'); $arr2 = larik('a' =› 10, 'd' =› 'qwert', 0 =› 100, 1 =› 200, 4 =› 400); $arr3 = array_merge($arr1, $arr2); gema(print_r($arr3, 1)); Keluaran Sebenarnya : Larik ( [a] =› 10 [b] =› asd [d] =› qwert [0] =› 100 [1] =› 200 [2] =› 400 ) Keluaran yang Diinginkan : Larik ( [a] =› 10 [b] =› asd [d] =› qwert [0] =› 100 [1] =› 200 [4] =› 400 ) - person Souvik; 23.01.2018
comment
Apakah hanya saya atau jawaban ini merupakan salinan kata demi kata dari jawaban yang telah diposting selama berbulan-bulan? stackoverflow.com/a/794356/151509 - person maryisdead; 09.05.2019

Anda dapat membuat objek lain yang mengirimkan panggilan ke metode ajaib ke objek yang mendasarinya. Inilah cara Anda menangani __get, tetapi untuk membuatnya berfungsi sepenuhnya, Anda harus mengganti semua metode ajaib yang relevan. Anda mungkin akan menemukan kesalahan sintaksis karena saya baru saja memasukkannya secara langsung.

class Compositor {
  private $obj_a;
  private $obj_b;

  public function __construct($obj_a, $obj_b) {
    $this->obj_a = $obj_a;
    $this->obj_b = $obj_b;
  }

  public function __get($attrib_name) {
    if ($this->obj_a->$attrib_name) {
       return $this->obj_a->$attrib_name;
    } else {
       return $this->obj_b->$attrib_name;
    }
  }
}

Semoga beruntung.

person Allain Lalonde    schedule 18.01.2009
comment
Implementasi lengkap mungkin memerlukan __isset(), __unset() dan mengimplementasikan antarmuka Interator. - person Kornel; 18.01.2009
comment
@porneL: apa itu Antarmuka Interator? - person Pim Jager; 19.01.2009
comment
Saya akan mengedit komentarnya, tetapi Anda tidak bisa melakukan itu. Saya pikir yang dia maksud adalah Iterator - person Allain Lalonde; 19.01.2009
comment
Saya sangat menyukai solusi Anda, Allain, tapi saya khawatir itu berarti kita harus menulis ulang seluruh aplikasi kita jika kita memutuskan untuk menggunakannya. - person Veynom; 19.01.2009
comment
Oke... kalau begitu pilih cara yang tidak memerlukan penulisan ulang secara menyeluruh. - person Allain Lalonde; 19.01.2009

Saya memahami bahwa menggunakan objek generik [stdClass()] dan menjadikannya sebagai array menjawab pertanyaan tersebut, tetapi menurut saya Compositor adalah jawaban yang bagus. Namun menurut saya ini memerlukan beberapa peningkatan fitur dan mungkin berguna bagi orang lain.

Fitur:

  • Tentukan referensi atau klon
  • Tentukan entri pertama atau terakhir yang akan diutamakan
  • Penggabungan beberapa objek (lebih dari dua) dengan kesamaan sintaksis dengan array_merge
  • Metode penautan: $obj->f1()->f2()->f3()...
  • Komposit Dinamis: $obj->merge(...) /* berfungsi di sini */ $obj->merge(...)

Kode:

class Compositor {

    protected $composite = array();
    protected $use_reference;
    protected $first_precedence;

    /**
     * __construct, Constructor
     *
     * Used to set options.
     *
     * @param bool $use_reference whether to use a reference (TRUE) or to copy the object (FALSE) [default]
     * @param bool $first_precedence whether the first entry takes precedence (TRUE) or last entry takes precedence (FALSE) [default]
     */
    public function __construct($use_reference = FALSE, $first_precedence = FALSE) {
        // Use a reference
        $this->use_reference = $use_reference === TRUE ? TRUE : FALSE;
        $this->first_precedence = $first_precedence === TRUE ? TRUE : FALSE;

    }

    /**
     * Merge, used to merge multiple objects stored in an array
     *
     * This is used to *start* the merge or to merge an array of objects.
     * It is not needed to start the merge, but visually is nice.
     *
     * @param object[]|object $objects array of objects to merge or a single object
     * @return object the instance to enable linking
     */

    public function & merge() {
        $objects = func_get_args();
        // Each object
        foreach($objects as &$object) $this->with($object);
        // Garbage collection
        unset($object);

        // Return $this instance
        return $this;
    }

    /**
     * With, used to merge a singluar object
     *
     * Used to add an object to the composition
     *
     * @param object $object an object to merge
     * @return object the instance to enable linking
     */
    public function & with(&$object) {
        // An object
        if(is_object($object)) {
            // Reference
            if($this->use_reference) {
                if($this->first_precedence) array_push($this->composite, $object);
                else array_unshift($this->composite, $object);
            }
            // Clone
            else {
                if($this->first_precedence) array_push($this->composite, clone $object);
                else array_unshift($this->composite, clone $object);
            }
        }

        // Return $this instance
        return $this;
    }

    /**
     * __get, retrieves the psudo merged object
     *
     * @param string $name name of the variable in the object
     * @return mixed returns a reference to the requested variable
     *
     */
    public function & __get($name) {
        $return = NULL;
        foreach($this->composite as &$object) {
            if(isset($object->$name)) {
                $return =& $object->$name;
                break;
            }
        }
        // Garbage collection
        unset($object);

        return $return;
    }
}

Penggunaan:

$obj = new Compositor(use_reference, first_precedence);
$obj->merge([object $object [, object $object [, object $...]]]);
$obj->with([object $object]);

Contoh:

$obj1 = new stdClass();
$obj1->a = 'obj1:a';
$obj1->b = 'obj1:b';
$obj1->c = 'obj1:c';

$obj2 = new stdClass();
$obj2->a = 'obj2:a';
$obj2->b = 'obj2:b';
$obj2->d = 'obj2:d';

$obj3 = new Compositor();
$obj3->merge($obj1, $obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj2:a, obj2:b, obj1:c, obj2:d
$obj1->c;

$obj3 = new Compositor(TRUE);
$obj3->merge($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, obj1:c, obj2:d
$obj1->c = 'obj1:c';

$obj3 = new Compositor(FALSE, TRUE);
$obj3->with($obj1)->with($obj2);
$obj1->c = '#obj1:c';
var_dump($obj3->a, $obj3->b, $obj3->c, $obj3->d);
// obj1:a, obj1:b, #obj1:c, obj2:d
$obj1->c = 'obj1:c';
person Ryan Schumacher    schedule 27.05.2009
comment
Sekadar untuk menunjukkan: Referensi lewat waktu panggilan ditandai sebagai tidak digunakan lagi di PHP 5.3.0 dan dihapus di PHP 5.4.0 (mengakibatkan peningkatan Kesalahan Fatal). Untuk memperbaiki masalah: Mengganti foreach($objects as &$object) $this->with(&$object); dengan foreach($objects as &$object) $this->with($object); akan memperbaiki masalah. Sumber: [php.net/manual/en/bahasa.references.pass. php] - person wes.hysell; 17.12.2013
comment
Selain itu: if($this->first_precedence) array_push($this->composite, &$object); else array_unshift($this->composite, &$object); harus diganti dengan if($this->first_precedence) array_push($this->composite, $object); else array_unshift($this->composite, $object); - person wes.hysell; 17.12.2013
comment
jadi untuk meringkas komentar Anda, hapus Ampersand (&) dari $object di dalam: foreach (komentar pertama)... array_push, array_unshift (komentar kedua) - person Chris; 05.04.2015
comment
@Chris Saya memperbarui kode untuk memperbaiki masalah sesuai komentar di atas. - person Ryan Schumacher; 08.04.2015
comment
Dalam kode 'Penggunaan', Anda salah mengeja Compositor sebagai Compositer - person Xesau; 26.09.2015
comment
Jika $first_precedence adalah boolean, maka Anda sebaiknya menetapkannya saja alih-alih menetapkan hasil atau TRUE === TRUE, yang selalu sama lagi: Boolean. - person kaiser; 23.09.2016

Solusi yang sangat sederhana mengingat Anda memiliki objek A dan B:

foreach($objB AS $var=>$value){
    $objA->$var = $value;
}

Itu saja. Anda sekarang memiliki objA dengan semua nilai dari objB.

person Jônatas Eridani    schedule 01.06.2010
comment
Mengapa Anda tidak melakukan saja: $objB = $objA; - person Scottymeuk; 13.06.2013

Kelas \ArrayObject mempunyai kemungkinan untuk menukar array saat ini untuk memutuskan referensi asli. Untuk melakukannya, ia hadir dengan dua metode praktis: exchangeArray() dan getArrayCopy(). Sisanya adalah array_merge() sederhana dari objek yang disediakan dengan properti publik ArrayObjects:

class MergeBase extends ArrayObject
{
     public final function merge( Array $toMerge )
     {
          $this->exchangeArray( array_merge( $this->getArrayCopy(), $toMerge ) );
     }
 }

Cara penggunaannya semudah ini:

 $base = new MergeBase();

 $base[] = 1;
 $base[] = 2;

 $toMerge = [ 3,4,5, ];

 $base->merge( $toMerge );
person Corelmax    schedule 12.08.2012
comment
Ini sebenarnya adalah jawaban yang diterima. Satu-satunya hal yang menyenangkan adalah jika merge($array) benar-benar meminta \ArrayObject juga. - person kaiser; 23.09.2016

solusi Untuk melestarikan, baik metode dan properti dari objek yang digabungkan adalah dengan membuat kelas kombinator yang bisa

  • ambil sejumlah objek di __construct
  • akses metode apa pun menggunakan __call
  • akses properti apa pun menggunakan __get

class combinator{
function __construct(){       
    $this->melt =  array_reverse(func_get_args());
      // array_reverse is to replicate natural overide
}
public function __call($method,$args){
    forEach($this->melt as $o){
        if(method_exists($o, $method)){
            return call_user_func_array([$o,$method], $args);
            //return $o->$method($args);
            }
        }
    }
public function __get($prop){
        foreach($this->melt as $o){
          if(isset($o->$prop))return $o->$prop;
        }
        return 'undefined';
    } 
}

penggunaan sederhana

class c1{
    public $pc1='pc1';
    function mc1($a,$b){echo __METHOD__." ".($a+$b);}
}
class c2{
    public $pc2='pc2';
    function mc2(){echo __CLASS__." ".__METHOD__;}
}

$comb=new combinator(new c1, new c2);

$comb->mc1(1,2);
$comb->non_existing_method();  //  silent
echo $comb->pc2;
person bortunac    schedule 08.06.2015
comment
Itu sangat pintar, angkat topi untuk itu. Saya rasa saya tidak akan merasa nyaman dengan metode yang tidak didefinisikan pada kelas objek yang dihasilkan. - person Slytherin; 09.12.2016
comment
terima kasih?.. untuk topinya... Itu hanya untuk bersenang-senang dan saya setuju dengan Anda tentang kenyamanan yang digunakan sebagian besar mengenai pelengkapan otomatis di netbeans atau editor lainnya - person bortunac; 10.12.2016

Saya akan menghubungkan objek kedua ke properti objek pertama. Jika objek kedua adalah hasil dari suatu fungsi atau metode, gunakan referensi. Mantan:

//Not the result of a method
$obj1->extra = new Class2();

//The result of a method, for instance a factory class
$obj1->extra =& Factory::getInstance('Class2');
person Adrian    schedule 07.12.2010

Untuk menggabungkan sejumlah objek mentah

function merge_obj(){
    foreach(func_get_args() as $a){
        $objects[]=(array)$a;
    }
    return (object)call_user_func_array('array_merge', $objects);
}
person bortunac    schedule 10.10.2016

Berikut adalah fungsi yang akan meratakan suatu objek atau array. Gunakan ini hanya jika Anda yakin kunci Anda unik. Jika Anda memiliki kunci dengan nama yang sama, kunci tersebut akan ditimpa. Anda harus menempatkan ini di kelas dan mengganti "Fungsi" dengan nama kelas Anda. Menikmati...

function flatten($array, $preserve_keys=1, &$out = array(), $isobject=0) {
        # Flatten a multidimensional array to one dimension, optionally preserving keys.
        #
        # $array - the array to flatten
        # $preserve_keys - 0 (default) to not preserve keys, 1 to preserve string keys only, 2 to preserve all keys
        # $out - internal use argument for recursion
        # $isobject - is internally set in order to remember if we're using an object or array
        if(is_array($array) || $isobject==1)
        foreach($array as $key => $child)
            if(is_array($child))
                $out = Functions::flatten($child, $preserve_keys, $out, 1); // replace "Functions" with the name of your class
            elseif($preserve_keys + is_string($key) > 1)
                $out[$key] = $child;
            else
                $out[] = $child;

        if(is_object($array) || $isobject==2)
        if(!is_object($out)){$out = new stdClass();}
        foreach($array as $key => $child)
            if(is_object($child))
                $out = Functions::flatten($child, $preserve_keys, $out, 2); // replace "Functions" with the name of your class
            elseif($preserve_keys + is_string($key) > 1)
                $out->$key = $child;
            else
                $out = $child;

        return $out;
}
person Community    schedule 15.05.2009

Mari kita tetap sederhana!

function copy_properties($from, $to, $fields = null) {
    // copies properties/elements (overwrites duplicates)
    // can take arrays or objects 
    // if fields is set (an array), will only copy keys listed in that array
    // returns $to with the added/replaced properties/keys
    $from_array = is_array($from) ? $from : get_object_vars($from);
    foreach($from_array as $key => $val) {
        if(!is_array($fields) or in_array($key, $fields)) {
            if(is_object($to)) {
                $to->$key = $val;
            } else {
                $to[$key] = $val;
            }
        }
    }
    return($to);
}

Jika itu tidak menjawab pertanyaan Anda, itu pasti akan membantu menjawabnya. Penghargaan untuk kode di atas diberikan kepada saya sendiri :)

person Rolf    schedule 02.09.2014

Cuplikan kode ini akan mengonversi data tersebut secara rekursif menjadi satu tipe (array atau objek) tanpa loop foreach yang disarangkan. Semoga ini bisa membantu seseorang!

Setelah Objek berada dalam format array, Anda dapat menggunakan array_merge dan mengkonversi kembali ke Objek jika perlu.

abstract class Util {
    public static function object_to_array($d) {
        if (is_object($d))
            $d = get_object_vars($d);

        return is_array($d) ? array_map(__METHOD__, $d) : $d;
    }

    public static function array_to_object($d) {
        return is_array($d) ? (object) array_map(__METHOD__, $d) : $d;
    }
}

cara prosedural

function object_to_array($d) {
    if (is_object($d))
        $d = get_object_vars($d);

    return is_array($d) ? array_map(__FUNCTION__, $d) : $d;
}

function array_to_object($d) {
    return is_array($d) ? (object) array_map(__FUNCTION__, $d) : $d;
}

Semua penghargaan diberikan kepada: Jason Oakley

person Drmzindec    schedule 12.10.2018

person    schedule
comment
Ini lebih cepat daripada jawaban yang diterima di PHP versi ‹ 7 (diperkirakan 50% lebih cepat). Namun dalam PHP ›= 7 jawaban yang diterima sekitar 400% lebih cepat. Lihat di sini: sandbox.onlinephpfunctions.com/code/ - person yunzen; 25.01.2017
comment
Bagaimana kita bisa menggunakan atau mendapatkan data gabungan di sini? - person ; 25.07.2018
comment
@ramedju Dalam contoh ini $objectB menyimpan data yang digabungkan. - person Kornel; 26.07.2018