Reputation: 3730
I am having a rather unique problem on which I'm pulling my teeth since days now.
What I am basically trying to do is to write an importer that can convert bbPress forums to my own Laravel forums. Importing users works perectly fine, the next step are categories. That is a big issue for the following reason:
bbPress can have unlimited sub forums, my own forums can only have 2 levels, meaning that each category can have one sub category. Sub categories cannot have any more sub categories. There is a tag system that replaces the need to have sub-categories of sub-categories. Example:
Support
Product line --> Available Tags: Product 1, Product 2..
I already created an array out of the bbPress export file and added all data that I need. It's important to keep in mind that the bbPress import can have unlimited sub categories, my example does not have too many, that might be different in other cases. The code needs to be able to handle then anyways. It is obvious to me that there will be somewhat of a recursive use, unfortnately I was not able to figure out where that should be and how it should be. My current example looks like this:
array:14 [
0 => array:5 [
"exists" => true
"post_id" => "2983"
"parent_id" => 3058
"data" => array:4 [
"name" => "Product Downloader"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 11
]
1 => array:5 [
"exists" => true
"post_id" => "2985"
"parent_id" => 2983
"data" => array:4 [
"name" => "Plugins"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 3
]
2 => array:5 [
"exists" => true
"post_id" => "2987"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 1"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 4
]
3 => array:5 [
"exists" => true
"post_id" => "3058"
"parent_id" => 0
"data" => array:4 [
"name" => "Support"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 1
]
4 => array:5 [
"exists" => true
"post_id" => "3065"
"parent_id" => 3058
"data" => array:4 [
"name" => "Protection Services"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 12
]
5 => array:5 [
"exists" => true
"post_id" => "3279"
"parent_id" => 3058
"data" => array:4 [
"name" => "Tutorials & How-Tos"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 13
]
6 => array:5 [
"exists" => true
"post_id" => "3471"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 2"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 5
]
7 => array:5 [
"exists" => true
"post_id" => "3472"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 3"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 6
]
8 => array:5 [
"exists" => true
"post_id" => "3475"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 4"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 7
]
9 => array:5 [
"exists" => true
"post_id" => "3476"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 5"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 8
]
10 => array:5 [
"exists" => true
"post_id" => "3979"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 6"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 9
]
11 => array:5 [
"exists" => true
"post_id" => "4683"
"parent_id" => 3058
"data" => array:4 [
"name" => "Protection Services Advanced"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 14
]
12 => array:5 [
"exists" => true
"post_id" => "6618"
"parent_id" => 2985
"data" => array:4 [
"name" => "Module 7"
"description" => ""
"icon" => ""
"locked" => 0
]
"id" => 10
]
]
For this issue, the only relevant keys are post_id
and parent_id
. parent_id
refers to post_id
, ignore the id
field. What should happen is the following:
The script creates all categories that do not have a parent id, meaning that the parent id is equal to 0. That is the easy part. After that it needs to do the logic with the parent_id. It needs to check if the category that should be imported has a parent_id. If so, it needs to check if that parent has another parent.
As explained, there should only be 2 levels, all other levels should be replaced with tags, that get assigned to the lowest parent that has been created.
It's important to keep in mind that the post_id is not always lower than the parent_id. One could have created the child first, then created the parent and then edited the child and set the parent. In that case the order would be messed up, that means that usort()
won't be of much help, at least from what I have tried. The code really needs to "resolve" the parent from the id.
The above example should come out like this:
Support
Product Downloader --> Tags: Plugins, Module 1 - Module 7
Protection Services
Protection Services Advanced
Tutorials & How-Tos
You can just add a placeholder/dummy code to the category and tag creation, e.g.
$newCategory = create_category($name, $parent_id)
and
$newTag = create_tag($name, $category_id);
Both of those calls will return the primary key of the created category/tag.
The issue is really about getting the data sorted and resolved correctly. I do not add code to the question due to the fact that there is currently no code that would be of any help, I had several attempts but none of them worked out as expected, they all fail at resolving the parents properly and decide if there needs to be a tag instead of creating another category. The json code for the example array is the following:
[{"exists":true,"post_id":"2983","parent_id":3058,"data":{"name":"Product Downloader","description":"","icon":"","locked":0},"id":6},{"exists":false,"post_id":"2985","parent_id":2983,"data":{"name":"Plugins","description":"","icon":"","locked":0},"id":8},{"exists":false,"post_id":"2987","parent_id":2985,"data":{"name":"Module 1","description":"","icon":"","locked":0},"id":9},{"exists":true,"post_id":"3058","parent_id":0,"data":{"name":"Support","description":"","icon":"","locked":0},"id":3},{"exists":true,"post_id":"3065","parent_id":3058,"data":{"name":"Protection Services","description":"","icon":"","locked":0},"id":4},{"exists":false,"post_id":"3279","parent_id":3058,"data":{"name":"Tutorials & How-Tos","description":"","icon":"","locked":0},"id":10},{"exists":false,"post_id":"3471","parent_id":2985,"data":{"name":"Module 2","description":"","icon":"","locked":0},"id":11},{"exists":false,"post_id":"3472","parent_id":2985,"data":{"name":"Module 3","description":"","icon":"","locked":0},"id":12},{"exists":false,"post_id":"3475","parent_id":2985,"data":{"name":"Module 4","description":"","icon":"","locked":0},"id":13},{"exists":false,"post_id":"3476","parent_id":2985,"data":{"name":"Module 5","description":"","icon":"","locked":0},"id":14},{"exists":false,"post_id":"3979","parent_id":2985,"data":{"name":"Module 6","description":"","icon":"","locked":0},"id":15},{"exists":true,"post_id":"4683","parent_id":3058,"data":{"name":"Protection Services Advanced","description":"","icon":"","locked":0},"id":5},{"exists":false,"post_id":"6618","parent_id":2985,"data":{"name":"Module 7","description":"","icon":"","locked":0},"id":16},{"exists":false,"post_id":"6618","parent_id":2985,"data":{"name":"Module 7","description":"","icon":"","locked":0},"id":16}]
# Edit: We can also have the input array have the keys represent the post_id if that is of any help, that would result in the following json data:
{"2983":{"exists":true,"post_id":"2983","parent_id":3058,"data":{"name":"Product Downloader","description":"","icon":"","locked":0},"id":6},"2985":{"exists":true,"post_id":"2985","parent_id":2983,"data":{"name":"Plugins","description":"","icon":"","locked":0},"id":8},"2987":{"exists":true,"post_id":"2987","parent_id":2985,"data":{"name":"Module 1","description":"","icon":"","locked":0},"id":9},"3058":{"exists":true,"post_id":"3058","parent_id":0,"data":{"name":"Support","description":"","icon":"","locked":0},"id":3},"3065":{"exists":true,"post_id":"3065","parent_id":3058,"data":{"name":"Protection Services","description":"","icon":"","locked":0},"id":4},"3279":{"exists":true,"post_id":"3279","parent_id":3058,"data":{"name":"Tutorials & How-Tos","description":"","icon":"","locked":0},"id":10},"3471":{"exists":true,"post_id":"3471","parent_id":2985,"data":{"name":"Module 2","description":"","icon":"","locked":0},"id":11},"3472":{"exists":true,"post_id":"3472","parent_id":2985,"data":{"name":"Module 3","description":"","icon":"","locked":0},"id":12},"3475":{"exists":true,"post_id":"3475","parent_id":2985,"data":{"name":"Module 4","description":"","icon":"","locked":0},"id":13},"3476":{"exists":true,"post_id":"3476","parent_id":2985,"data":{"name":"Module 5","description":"","icon":"","locked":0},"id":14},"3979":{"exists":true,"post_id":"3979","parent_id":2985,"data":{"name":"Module 6","description":"","icon":"","locked":0},"id":15},"4683":{"exists":true,"post_id":"4683","parent_id":3058,"data":{"name":"Protection Services Advanced","description":"","icon":"","locked":0},"id":5},"6618":{"exists":true,"post_id":"6618","parent_id":2985,"data":{"name":"Module 7","description":"","icon":"","locked":0},"id":16}}
$categoriesMapped = [];
$posts = $categoriesToBeProcessed;
// find all the categories
foreach ($posts as $post) {
if (!$post['parent_id']) {
$categoryCreate = $this->category->create($post['data']);
$categories[] = $categoryCreate;
$categoriesMapped[$categoryCreate->id] = $post['post_id'];
}
}
// now find all the sub-categories and tags
foreach ($categories as $category) {
foreach ($posts as $post) {
if ($post['parent_id'] == $categoriesMapped[$category->id]) {
$data = $post['data'];
$data['parent_id'] = $category->id;
$thisSubcategory = $this->category->create($data);
$thisSubcategory->tags = $this->find_tags($categoriesMapped, $post['post_id'], $posts);
$category->subcategories[] = $thisSubcategory;
}
}
}
private function find_tags($categoriesMapped, $id, $posts) {
$tags = array();
foreach ($posts as $post) {
if ($post['parent_id'] == $categoriesMapped[$id]) {
$tag = $this->tag->create($post['data']);
$tags[] = $tag;
$tag->category()->associate($id);
// any subcategories below this?
// TODO: How to fix this?
$tags = array_merge($tags, $this->find_tags($categoriesMapped, $post['post_id'], $posts));
}
}
return array_unique($tags, SORT_REGULAR);
}
$categoriesMapped = [];
$posts = $categoriesToBeProcessed;
// find all the categories
foreach ($posts as $post) {
if (!$post['parent_id']) {
$categoryCreate = $this->category->create($post['data']);
$categories[] = $categoryCreate;
$categoriesMapped[$categoryCreate->id] = $post['post_id'];
}
}
// now find all the sub-categories
foreach ($categories as $category) {
foreach ($posts as $post) {
if ($post['parent_id'] == $categoriesMapped[$category->id]) {
$data = $post['data'];
$data['parent_id'] = $category->id;
$thisSubcategory = $this->category->create($data);
$category->subcategories[] = $thisSubcategory;
$categoriesMapped[$thisSubcategory->id] = $post['post_id'];
}
Any help is highly appreciated.
Upvotes: 0
Views: 75
Reputation: 147156
I think code modeled on this should do what you want:
$posts = json_decode('[{"exists":true,"post_id":"2983","parent_id":3058,"data":{"name":"Product Downloader","description":"","icon":"","locked":0},"id":6},{"exists":false,"post_id":"2985","parent_id":2983,"data":{"name":"Plugins","description":"","icon":"","locked":0},"id":8},{"exists":false,"post_id":"2987","parent_id":2985,"data":{"name":"Module 1","description":"","icon":"","locked":0},"id":9},{"exists":true,"post_id":"3058","parent_id":0,"data":{"name":"Support","description":"","icon":"","locked":0},"id":3},{"exists":true,"post_id":"3065","parent_id":3058,"data":{"name":"Protection Services","description":"","icon":"","locked":0},"id":4},{"exists":false,"post_id":"3279","parent_id":3058,"data":{"name":"Tutorials & How-Tos","description":"","icon":"","locked":0},"id":10},{"exists":false,"post_id":"3471","parent_id":2985,"data":{"name":"Module 2","description":"","icon":"","locked":0},"id":11},{"exists":false,"post_id":"3472","parent_id":2985,"data":{"name":"Module 3","description":"","icon":"","locked":0},"id":12},{"exists":false,"post_id":"3475","parent_id":2985,"data":{"name":"Module 4","description":"","icon":"","locked":0},"id":13},{"exists":false,"post_id":"3476","parent_id":2985,"data":{"name":"Module 5","description":"","icon":"","locked":0},"id":14},{"exists":false,"post_id":"3979","parent_id":2985,"data":{"name":"Module 6","description":"","icon":"","locked":0},"id":15},{"exists":true,"post_id":"4683","parent_id":3058,"data":{"name":"Protection Services Advanced","description":"","icon":"","locked":0},"id":5},{"exists":false,"post_id":"6618","parent_id":2985,"data":{"name":"Module 7","description":"","icon":"","locked":0},"id":16},{"exists":false,"post_id":"6618","parent_id":2985,"data":{"name":"Module 7","description":"","icon":"","locked":0},"id":16}]');
$categories = array();
class Category {
public $name;
public $id;
public $subcategories;
public function __construct($post) {
$this->name = $post->data->name;
$this->id = $post->post_id;
$this->subcategories = array();
}
}
class Subcategory {
public $name;
public $id;
public $tags;
public function __construct($post) {
$this->name = $post->data->name;
$this->id = $post->post_id;
$this->tags = array();
}
}
class Tag {
public $name;
public $id;
public function __construct($post) {
$this->name = $post->data->name;
$this->id = $post->post_id;
}
}
function find_tags($id, $posts) {
$tags = array();
foreach ($posts as $post) {
if ($post->parent_id == $id) {
$tags[] = new Tag($post);
// any subcategories below this?
$tags = array_merge($tags, find_tags($post->post_id, $posts));
}
}
return array_unique($tags, SORT_REGULAR);
}
// find all the categories
foreach ($posts as $post) {
if (!$post->parent_id) {
$categories[] = new Category($post);
}
}
// now find all the sub-categories and tags
foreach ($categories as $category) {
foreach ($posts as $post) {
if ($post->parent_id == $category->id) {
$thisSubcategory = new Subcategory($post);
$thisSubcategory->tags = find_tags($post->post_id, $posts);
$category->subcategories[] = $thisSubcategory;
}
}
}
print_r($categories);
If you need to create all the subcategories before creating any of the tags, you could change the last loop (prefaced by // now find all the sub-categories and tags) to these two. This is quite a bit less efficient which is why I didn't write it this way in the first place.
// now find all the sub-categories
foreach ($categories as $category) {
foreach ($posts as $post) {
if ($post->parent_id == $category->id) {
$category->subcategories[] = new Subcategory($post);
}
}
}
// now find all the sub-categories and tags
foreach ($categories as $category) {
foreach ($category->subcategories as $subcategory) {
$subcategory->tags = find_tags($subcategory->id, $posts);
}
}
Upvotes: 1