Apa metode terbaik untuk menggabungkan dua objek PHP?

222

Kami memiliki dua objek PHP5 dan ingin menggabungkan konten satu menjadi yang kedua. Tidak ada konsep subclass di antara mereka sehingga solusi yang dijelaskan dalam topik berikut ini tidak dapat diterapkan.

Bagaimana Anda 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;

Catatan:

  • Ini adalah objek, bukan kelas.
  • Benda-benda itu berisi cukup banyak bidang sehingga pendahuluan akan cukup lambat.
  • Sejauh ini kami mempertimbangkan untuk mengubah objek A dan B menjadi array kemudian menggabungkannya menggunakan array_merge () sebelum mentransformasikannya menjadi objek tetapi kami tidak dapat mengatakan kami bangga jika ini.
Veynom
sumber
30
"Benda-benda itu mengandung cukup banyak bidang sehingga pendahuluan akan sangat lambat." - Komputer cukup cepat, 'cukup lambat' seringkali cukup cepat.
Sean McSomething

Jawaban:

435

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

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

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

flochtililoch
sumber
1
Anda juga dapat menggunakan array_merge_recursive untuk memiliki perilaku penyalinan yang dalam. 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
Vincent Pazeller
12
Objek yang dihasilkan dari ini akan menjadi instance dari stdclass. Sementara itu "bekerja" dalam arti pada objek dengan metode, itu secara efektif merusak objek dalam kasus itu (dengan menghapus metode).
Brilliand
Ini berguna untuk mengembalikan beberapa hasil dalam satu fungsi (Dan mengembalikan hanya objek dengan pasangan nilai-kunci.)
Leonel Atencio
1
Ini tidak akan berfungsi jika ada kunci integer di objek. Pertimbangkan contoh berikut: $ arr1 = array ('a' => 9, 'b' => 'asd'); $ arr2 = array ('a' => 10, 'd' => 'qwert', 0 => 100, 1 => 200, 4 => 400); $ arr3 = array_merge ($ arr1, $ arr2); echo (print_r ($ arr3, 1)); Output Aktual: Array ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [2] => 400) Output yang Diinginkan: Array ([a] => 10 [b] => asd [d] => qwert [0] => 100 [1] => 200 [4] => 400)
Souvik
2
Apakah hanya saya atau ini jawaban salinan kata demi kata dari jawaban yang sudah diposting selama berbulan-bulan? stackoverflow.com/a/794356/151509
maryisdead
28

Anda bisa membuat objek lain yang mengirimkan panggilan ke metode ajaib ke objek yang mendasarinya. Begini cara Anda menangani __get, tetapi untuk membuatnya bekerja sepenuhnya Anda harus mengganti semua metode sihir yang relevan. Anda mungkin akan menemukan kesalahan sintaksis karena saya baru saja memasukkannya dari atas kepala saya.

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 berhasil.

Allain Lalonde
sumber
Implementasi lengkap mungkin perlu __isset (), __unset () dan mengimplementasikan antarmuka Interator.
Kornel
@porneL: apa itu Antarmuka Interator?
Pim Jager
2
Saya akan mengedit komentarnya, tetapi Anda tidak dapat melakukannya. Saya pikir maksudnya Iterator
Allain Lalonde
Saya sangat menyukai solusi Anda, Allain, tapi saya khawatir itu berarti kami harus menulis ulang seluruh aplikasi kami jika kami memutuskan untuk menggunakannya.
Veynom
3
Oke ... lalu pilih cara yang tidak membutuhkan penulisan ulang yang lengkap.
Allain Lalonde
25
foreach($objectA as $k => $v) $objectB->$k = $v;
Kornel
sumber
6
Ini lebih cepat dari jawaban yang diterima dalam versi PHP <7 (diperkirakan 50% lebih cepat). Tetapi dalam PHP> = 7 jawaban yang diterima adalah sekitar 400% lebih cepat. Lihat di sini: sandbox.onlinephpfunctions.com/code/…
yunzen
Bagaimana kami bisa menggunakan atau mendapatkan data yang digabungkan di sini?
1
@ramedju Dalam contoh ini $objectBmenampung data yang digabungkan.
Kornel
10

Saya mengerti bahwa menggunakan objek generik [stdClass ()] dan menuangnya sebagai array menjawab pertanyaan, tetapi saya pikir Compositor adalah jawaban yang bagus. Namun saya merasa itu bisa menggunakan beberapa peningkatan fitur dan mungkin berguna untuk orang lain.

Fitur:

  • Tentukan referensi atau klon
  • Tentukan entri pertama atau terakhir untuk diutamakan
  • Beberapa (lebih dari dua) objek bergabung dengan sintaks yang mirip dengan array_merge
  • Penautan metode: $ 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;
    }
}

Pemakaian:

$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';
Ryan Schumacher
sumber
2
Just to point out: Referensi lewat waktu panggilan ditandai sebagai tidak digunakan lagi dalam PHP 5.3.0 dan dihapus dalam PHP 5.4.0 (menghasilkan Fatal Error yang meningkat). Untuk memperbaiki masalah: Mengganti foreach($objects as &$object) $this->with(&$object);dengan foreach($objects as &$object) $this->with($object);memperbaiki masalah. Sumber: [ php.net/manual/en/language.references.pass.php]
wes.hysell
2
Selain itu: if($this->first_precedence) array_push($this->composite, &$object); else array_unshift($this->composite, &$object);harus diganti denganif($this->first_precedence) array_push($this->composite, $object); else array_unshift($this->composite, $object);
wes.hysell
1
jadi untuk meringkas komentar Anda, hapus Ampersand (&) dari $ object inside: foreach (komentar pertama) ... array_push, array_unshift (komentar kedua)
Chris
1
@ Chris Saya memperbarui kode untuk memperbaiki masalah per komentar di atas.
Ryan Schumacher
Dalam kode 'Penggunaan' Anda, Anda salah mengeja
Komponis
7

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.

Jônatas Eridani
sumber
Mengapa Anda tidak melakukannya: $ objB = $ objA;
Scottymeuk
2

The \ArrayObjectkelas memiliki kemungkinan untuk bertukar array saat ini untuk memutuskan asli referensi . Untuk melakukannya, ia datang dengan dua metode praktis: exchangeArray()dan getArrayCopy(). Sisanya sederhana array_merge()dari objek yang disediakan dengan ArrayObjectproperti publik:

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

Penggunaannya semudah ini:

 $base = new MergeBase();

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

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

 $base->merge( $toMerge );
Corelmax
sumber
Ini seharusnya jawaban yang diterima . Satu-satunya hal yang baik adalah jika merge($array)benar-benar meminta \ArrayObjectjuga.
kaiser
2

solusi Untuk mempertahankan, baik metode dan properti dari onjects yang digabung adalah membuat kelas kombinator yang bisa

  • ambil sejumlah objek di __construct
  • mengakses metode apa saja menggunakan __call
  • mengakses semua properti 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;
bortunac
sumber
Itu sangat pintar, untuk itu. Saya tidak berpikir saya akan merasa nyaman dengan metode yang tidak didefinisikan pada kelas objek yang dihasilkan sekalipun.
Slytherin
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
bortunac
1

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

//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');
Adrian
sumber
1

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);
}
bortunac
sumber
0

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

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;
}

sumber
0

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, pasti akan membantu menuju jawabannya. Kredit untuk kode di atas berlaku untuk saya sendiri :)

Rolf
sumber
0

Potongan kode ini akan secara rekursif mengkonversi data itu menjadi satu jenis (array atau objek) tanpa loop foreach bersarang. Semoga ini bisa membantu seseorang!

Setelah Object dalam format array, Anda dapat menggunakan array_merge dan mengkonversi kembali ke Obyek 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 kredit diberikan kepada: Jason Oakley

Drmzindec
sumber