Filip Pacurar
Filip Pacurar

Hello, I've written a new tutorial on how to use the new import and export feature of October - it's on OctoHelp

http://octo-help.com/how-to-use-the-new-import-and-export-feature-in-october/

aleksandr.tsiolko418
aleksandr.tsiolko418

Thanks for cool articles. Can you say some words about import "has many" relations to database. As i understand, all logics in importModel class, but what about "Has many"? How talk to controller import fields correctly? Thanks, Filip for your work.

Filip Pacurar
Filip Pacurar

I will see what i can write after work hours :)

alxy
alxy

One quick note: Models should have a singular name. So your model's name should be Entry

Filip Pacurar
Filip Pacurar

yeah, that's true but i wanted to name them the same - controller + model :) it was not by mistake, i just wanted to name them that way.

Scott
Scott

Filip Pacurar said:

yeah, that's true but i wanted to name them the same - controller + model :) it was not by mistake, i just wanted to name them that way.

This is a bad practice for a couple of reasons... First, if you want to use your model in your controller, you now have to use aliases which gets annoying. But the bigger problem is that a model in active record (like eloquent) refers to a single row in the database. alxy is correct, models should be singular, controllers should be plural.

Last updated

Keios
Keios

Lol:

$car = Cars::find(1);
var_dump($cars instanceof Cars); // true

I just wanted to name them that way.

Ignoring conventions on purpose and basically on a whim is not only bad practice - it's evil. You break the implicit semantic contract between people using your code in community that generally agrees with convention. If I have classes 'Car' and 'Cars' then I know without even looking at namespaces what type of classes I have - an entity and a controller. You present no reason to break convention and moreover you publish tutorial material promoting your mistake without mentioning in said article that you are breaking a convention and that is on a whole new level of evil.

Last updated

Filip Pacurar
planetadeleste
planetadeleste

Import / Export with relationship!

In this tutorial I will try to explain how to export / import data with related models. Before continue, please read the excellent tutorial "How to use the new Import and Export feature in October".


Plugin settings

In a basic example, we have the models Book, Author, Contact and Category

Table structure

books
    id
    title
    description
    author_id

book_categories
    book_id
    category_id

categories
    id
    name

authors
    name
    email

contacts
    name
    author_id

Models relationships

Book
    public $belongsToMany = [
        'categories' => [
            'Acme\Books\Models\Category',
            'table' => 'book_categories'
        ]
    ];

    public $belongsTo = [
        'author' => ['Acme\Books\Models\Author']
    ];

Author
    public $hasMany = [
        'contacts' => ['Acme\Books\Models\Contact'],
        'books' => ['Acme\Books\Models\Book']
    ];

Contact
    public $belongsTo = [
        'author' => ['Acme\Books\Models\Author']
    ];

Controller yaml file for Import/Export

File controllers/books/config_import_export.yaml

# ===================================
#  Import/Export Behavior Config
# ===================================

import:
    title: Import books
    modelClass: Acme\Books\Models\BookImport
    list: $/acme/books/models/book/import_export_columns.yaml

export:
    title: Export books
    modelClass: Acme\Books\Models\BookExport
    list: $/acme/books/models/book/import_export_columns.yaml

I prefer to use a separate yaml file for import/export than controller column lists
File acme/books/models/book/import_export_columns.yaml

columns:
    id:
        label: ID
    title:
        label: Book title
    description:
        label: Book description
    author:
        label: Author
    categories:
        label: Categories that belongs

Import / Export models

Export model
<?php namespace Acme\Books\Models;

use Backend\Models\ExportModel;

/**
 * BookExport Model
 */
class BookExport extends ExportModel
{

    public function exportData($columns, $sessionKey = null)
    {
        $books = Book::with([
            'author.contacts'  => function($query){ $query->addSelect(['name', 'author_id']); },
            'author' => function($query){ $query->addSelect(['id', 'name', 'email']); },
            'categories' => function($query){ $query->addSelect(['name']); }
        ])->get();
        $books->each(function($book) use ($columns) {
            /** @var Book $book */
            $book->addVisible($columns);
        });
        $collection = collect($books->toArray());
        $data = $collection->map(function ($item) {
            if(is_array($item)){
                foreach($item as $key => $value) {
                    if(is_array($value)) {
                        $item[$key] = json_encode($value);
                    }
                }
            }
            return $item;
        });

        return $data->toArray();
    }
}
Import model
<?php namespace Acme\Books\Models;

use Backend\Models\ImportModel;

/**
 * BookImport Model
 */
class BookImport extends ImportModel
{
    /**
     * @var array The rules to be applied to the data.
     */
    public $rules = ['name' => 'required', 'author' => 'required', 'categories' => 'required'];

    public function importData($results, $sessionKey = null)
    {
        foreach ($results as $row => $data) {
            try {
                // First create the Author if not exists
                $author_data = (!is_array($data['author'])) ? json_decode($data['author']) : $data['author'];
                $modelAuthor = null;
                if(is_array($author_data) && count($author_data)) {
                    $modelAuthor = Author::whereEmail($author_data['email']);
                    if(!$modelAuthor) {
                        $modelAuthor = Author::create(['name' => $author_data['name'], 'email' => author_data['email']]);
                        if(array_key_exists('contacts', $author_data)) {
                            foreach($author_data['contacts'] as $author_contact){
                                Contact::create(['name' => $author_contact['name'] , 'author_id' => $modelAuthor->id ]);
                            }
                        }
                    }
                }

                // Create/Update the Book model
                $book_data = [
                    'title' => $data['title'],
                    'description' => $data['description']
                ];
                if($modelAuthor) {
                    $book_data['author_id'] = $modelAuthor->id;
                }
                if(array_key_exists('id', $data)){
                    $book = Book::find($data['id']);
                    $book->update($book_data);
                } else {
                    $book = Book::create($book_data);
                }

                // Add categories
                $categories_data = (!is_array($data['categories'])) ? json_decode($data['categories']) : $data['categories'];
                if (is_array($categories_data) && count($categories_data)) {
                    $cd = collect($categories_data);
                    $cd->forget('pivot');
                    $cd->each(
                        function ($category) use ($book) {
                            $name = $category->name;
                            if (!$book->categories()->whereName($name)->count()) {
                                $modelCategory = Category::whereName($name)->first();
                                if (!$modelCategory) {
                                    $modelCategory = Category::create($category->toArray());
                                }
                                $book->categories()->attach($modelCategory->id);
                            }
                        }
                    );
                }

                if (array_key_exists('id', $data)) {
                    $this->logUpdated();
                } else {
                    $this->logCreated();
                }

            } catch (\Exception $ex) {
                $this->logError($row, $ex->getMessage());
            }
        }
    }
}

Sorry my english. I hope to help someone.

Thanks

Last updated

chris10207
chris10207

thanks planetadeleste, very useful code :)

i was wondering 2 things :

  • how the book import files is looking ? do you have an example ?
  • was is the purpose of the function collect() used for the categories ?

thanks

planetadeleste
planetadeleste

@chris10207; The exported csv file look something like this:

Id,Title,Description,Author,Category
1,"Code Smart","Code Smart contains everything you need to know to get building your own Laravel applications. It's written for human beings using simple language, as if we were chatting together in a pub.","{""name"":""Dayle Rees"",""email"":""info@daylerees.com"",""contacts"":"[{""name"":""Contact Name 1""}, {""name"":""Contact Name 2""}]"}","[{""name"":""Programming""},{""name"":""PHP""},{""name"":""Laravel 5""}]"

The use of collect is to have more control over category data, using Collections. But you can use native php functions.

chris10207
chris10207

Hi thanks for this, and how about the original import file ? im struggling with the is_array() and json things in the import file.

planetadeleste
planetadeleste

@chris10207 I use json_encode and json_decode because I don't found another way to store relation data, like author and/or category. If your export/import data doesn't have or need relationship, you don't need the use json encoder/decoder.

The use of is_array() is only to prevent php Exceptions in json_decode(). But I think this data always be a string.

Sorry if my English is not quite right.

chris10207
chris10207

ok got it, thanks for the explanations

thepottingshed
thepottingshed

Anyone have any idea why my slug column won't export?

Tschallacka
Tschallacka

I tried

Nope, my crystal ball doesn't show your code. Make a new topic in which you outline your code and problem.
Prefably in a http://stackoverflow.com/help/mcve way...

Last updated

thepottingshed
thepottingshed

Yep sorry I was having a brain fart. Love the GIF though!

PolloZen
PolloZen

I think the import post tool included in Blog Plugin es great, but I can´t find the way to import the featured images from my wordpress blog or joomla site. Every document I readed talks just about import text, but nothing about import post images, specially the featured image. Anyone knows how to do this? Do I need to develop a plugin for this purpose?

planetadeleste
planetadeleste

@PolloZen, in your case, I think, the best way is upload the images in some temporary folder, them in your import script method, find for the reference to the current image with the uploaded file.
Read the Attachments documentation.

The Import/Export utility is for CSV files, is only in your brain how you want to process the imported data.

mano
mano

can any one point me to export and import a single record of a list ?

1-20 of 26