migration to populate existing fields - php

I have a model and ready-made data in a table. In this model, I added a new field and made a connection with another table.
And in order not to manually fill in these fields for each record, I want to create a migration that will automatically fill in this field for all records.
Relationship table has two fields: post_id and author_id.
I'm creating a migration where I get all existing post_id and try to add value to author_id:
public function safeUp()
{
/**
* #var Posts[] $posts
*/
$posts = Posts::find()->all();
foreach ($posts as $post) {
$item = new PostAuthor();
$item->setAttribute('post_id', $post->id);
$item->setAttribute('author_id', 2);
$item->save();
}
}
Now everything is working, and all existing posts are given an author_id with a value of 2.
But I would like to refine it a little, author_id can have a value from 1 to 4, and I want different values ​​​​to be added to each post in the migration.
Let's say the first post gets author_id: 1
The second post will get author_id: 1, author_id: 2
Third post author_id: 1, author_id: 2, author_id: 3
And the fourth post respectively author_id: 1, author_id: 2, author_id: 3, author_id: 4
But how can I do this, because now all my posts will receive the same values?
So far, the only working option I have is this one:
public function safeUp()
{
/**
* #var Posts[] $posts
*/
$posts = Posts::find()->all();
if (isset($posts[1])) {
$item = new PostAuthor();
$item->setAttribute('post_id', $posts[1]->id);
$item->setAttribute('author_id', 1);
$item->save();
}
if (isset($posts[2])) {
$item = new PostAuthor();
$item->setAttribute('post_id', $posts[2]->id);
$item->setAttribute('author_id', 1);
$item->save();
$item2 = new PostAuthor();
$item2->setAttribute('post_id', $posts[2]->id);
$item2->setAttribute('author_id', 2);
$item2->save();
}
....
}
But in order to create for the first five records, there will be a lot of copying code, is it possible to do it all somehow differently??

If i understood it correctly, you want to create as many PostAuthors as the respective PostId.
$posts = Posts::find()->all();
foreach ($posts as $index => $post) {
for($i = 0; $i < $index; $i++ ) {
$item = new PostAuthor();
$item->setAttribute('post_id', $posts->id);
$item->setAttribute('author_id', $i+1);
$item->save();
}
}

Related

Why the name of trucks are same in 2nd and 3rd record of company data retrieval?

My data in database:
My objective is to retrieve all data of company collection (MongoDB) data from the database. The company collection holds one or many trucks. I have implemented one-to-many reference between company and trucks collection. That is working fine.
I am using query builder to get all the data. But, my code is not giving me all the Trucks of the specific company. It is retrieving only one truck name for specific documents.
My API code is checking the length of the truck's array and storing the name for the truck on ta[] array. Code is not written nicely as I have tried so many methods and I am frustrated now.
How can I retrieve my data?
My API:
/**
* #Route("/koco/get/company/query", name="queryToGetCompany")
* #Template()
*/
public function queryForCompanyAction()
{
$checkarray = array();
$dm = $this->get('doctrine_mongodb')->getManager();
$qb = $dm->createQueryBuilder(Company::class);
$qb->find(Company::class);
$query = $qb->getQuery();
$comapnies = $query->execute();
foreach ($comapnies as $company)
{
$objectId = $company->getId();
$objectName = $company->getName();
$objectAddress = $company->getAddress();
// for length
$len = count($company->getTrucks());
echo $len;
// For trucks
$Trucks = $company->getTrucks();
foreach ($Trucks as $truckname)
{
$ta = array();
for($i=0;$i< $len;$i++){
$object = new Truck();
$object = $truckname->getName();
$ta[] = $object;
}
}
$checkarray[] = array(
'Id' => $objectId,
'Name' =>$objectName,
'Address' =>$objectAddress,
'Trucks' => $ta,
);
}
$data = [
'Comapnies' => $checkarray,
];
return new JsonResponse($data);
}
My results from the API:
The 2nd and third companies are giving me the same records for the name of trucks, but in my database the truck names are different.
Your foreach and your for loop are colliding, in conjunction with your array being emptied inside the foreach loop. If you reset your array before the foreach loop, not inside, and also just use a foreach without the for, I think this is what you want.
What is happening in your code as presented in the question is that the array is wiped out between trucks, so you only get the last truck. Additionally, because of the manually counted for loop, the number of copies of the last truck is equal to the total number of trucks associated with each company.
This is my suggestion based on what you have shown, replacing the entire foreach loop with this code.
$ta = array();
foreach ($Trucks as $truckname)
{
$object = new Truck();
$object = $truckname->getName();
$ta[] = $object;
}

Laravel posts types categories

I need an array with categories, each category has 4 posts: an array with two posts with type_1 and array with two posts with type_2. Without type_3.
The display should look like this (not using blade):
category name_1
post type_1 name
post type_1 name
post type_2 name
post type_2 name
category name_2
post type_1 name
post type_1 name
post type_2 name
post type_2 name
posts_table:
$table->enum('type', ['type_1', 'type_2', 'type_3'])->default('type_1');
$table->unsignedBigInteger('category_id')->index();
$table->foreign('category_id')->references('id')->on('categories');
categories_table:
$table->string('name');
Category model:
return $this->hasMany('App\Models\Post');
Post model:
return $this->belongsTo('App\Models\Category');
I tried:
$categories = ['category_1', 'category_2'];
foreach ($categories as $cat) {
$postsType1[$cat->name] = $cat->posts()->where('type', 'type_1')->latest()->limit(2)->get();
$postsType2[$cat->name] = $cat->posts()->where('type', 'type_2')->latest()->limit(2)->get();
}
But these are two separate arrays in categories. I need one categories array with 2 posts of each type.
You can leverage the collection groupBy() with multiple conditions
$posts = Post::query()
->latest()
->get()
->groupBy([
function($post) {
return $post->category->name;
},
function($post) {
return $post->type;
},
])
->map(function($records, $category) {
return $records->sortKeys()->map(function($rows, $type){
return $rows->take(2)->all();
});
})
->all();
And if you want to just group by the Category name and sort by Post type
$posts = Post::get()
->group(function($post){
return $post->category->name;
})
-> map(function($value, $key){
return $value->sortBy('type')->sortByDesc('created_at')->take(2);
})
->all();
UPDATE
To avoid fetching all Post records without any limit another option would be to:
create a composite index on the posts table for better query performance
//in migration file for the posts table
$table->index(['type', 'category_id']);
define 3 relationships on the Category model
class Category extends Model
{
public function type1News()
{
return $this->hasMany(Post::class)->where('type', 'type_1')->latest();
}
public function type2News()
{
return $this->hasMany(Post::class)->where('type', 'type_2')->latest();
}
public function type3News()
{
return $this->hasMany(Post::class)->where('type', 'type_3')->latest();
}
}
Then to get the data
$categories = Cache::remember('categoriesWithLatestNews', 3600, function() {
$cats = Category::get();
$cats->each->load([
'type1News' => function($query){
return $query->take(2);
},
'type2News' => function($query){
return $query->take(2);
},
'type3News' => function($query){
return $query->take(2);
}
]);
return $cats;
});
With 1 million records in the posts table the response time was 2.75 seconds for the first time. (Without composite index the response time is over 120s)
Then after it is cached for the specified seconds so response time is in ms.
Though this approach requires multiple database queries a total of 31 (10 category records) queries: 1 to get all categories and then 3 for each categories to get the relations.

Laravel how to get sum price for domain prices table

I am using Laravel 7.0.
My domain_prices table has price, duration, addPrice columns.
I created DomainPrice model.
Domain price for specific duration is price + addPrice.
But for 5 years duration, I need to sum all prices and all addPrices for 1, 2, 3, 4, 5 years.
I set custom attribute to get totalPrice as following:
public function getTotalPriceAttribute()
{
return $this->price+$this->addPrice;
}
I wanted to make another custom attribute to get sumPrice as following:
public function getSumPriceAttribute()
{
$sumPrice = 0;
$prices = $this->where('Duration', '<=', $this->Duration)->get(); // I removed domain filter query here.
foreach($prices as $price)
{
$sumPrice += $price->totalPrice;
}
return $sumPrice;
}
But this didn't return exact results.
Please teach me with fancy approach. Thanks.
I think you used the wrong variable here when you sum your $sumPrice. you should change $subPrice in foreach loop to $sumPrice
public function getSumPriceAttribute()
{
$sumPrice = 0;
$prices = $this->where('Duration', '<=', $this->Duration)->get(); // I removed domain filter query here.
foreach($prices as $price)
{
$sumPrice += $price->totalPrice; // change $subPrice to $sumPrice
}
return $sumPrice;
}

Showing Category and sub category when admin browse orders

When customer submit his order it is stored in database table orders in array. The array looks like:
{
"15": // product_id
{
"title":"Test of title", // product_title
"description":"Test of description", // product_description
"quantity":1,
"price":132
},
"shipping": // selected shipping information
{
"title":"Normal Delivery" - $10.00",
"price":10
}
}
Then in my Order Model I have this
public function getOrderData($data) {
return json_decode($data);
}
In the controller
public function orders() {
$orders = Order::select('*')->paginate(10);
return View::make('admin.orders', [
'orders' => $orders
]);
}
On the view I've made one foreach and displaying information for the order from this array like
#foreach($order->getOrderData($order->data) as $itemId => $item)
Title of product
Description of product
Price of product
#endforeach
What I want to make is same view but in front of the Title of product to show also Category name and sub-category name, like
#foreach($order->getOrderData($order->data) as $itemId => $item)
Category 1 -> Sub_category 1 -> Title of product
.... // other info
#endforeach
Problem: I don't have anything for category and sub-category in that array and in orders table at all. Tried to join tables Category and Sub-Category but they doesn't have columns in order table only in product table I store category_id and sub_category_id.
How can I show them?
Update 2: Changed getOrderData in Order Model like this
public function getOrderData($data) {
$dataArray = json_decode($data, true);
$data = json_decode($data);
$arrayKeys = array_keys($dataArray);
$arrayKeys = array_filter($arrayKeys, function($value) {
return ($value !== 'shipping');
});
$productIds = implode(',', $arrayKeys);
$products =
DB::table('products')
->join('category', 'products.product_id', '=', 'category.category_id')
->join('sub_category', 'products.sub_cat_id', '=', 'sub_category.sub_cat_id')
->whereIn('product_id',$productIds) ;
foreach ($products as $item) {
if(!in_array($item['product_id'], $arrayKeys)) continue;
$dataArray[$item['product_id']]['category'] = $item['category']; // depending on the implementation $item may be array or an object! so this code may vary
$dataArray[$item['product_id']]['subcategory'] = $item['subcategory'];
}
return (object) $dataArray;
//return json_decode(json_encode($dataArray));
}
Got error:
array_keys() expects parameter 1 to be array, object given
Update: This query in phpmyadmin is returning me all the information for product with id=9 + all information for category and sub-category
SELECT *
FROM products AS p
LEFT JOIN category AS c ON p.category_id =1
LEFT JOIN sub_category AS sc ON p.sub_cat_id =1
WHERE p.product_id =9
I can give you this solution:
After getting the result for the order from the table you can iterate over the order to get productIDs. Then do select query like this one:
SELECT fields FROM products AS p
JOIN categories AS c ON ...
JOIN sub_categories AS sc ON ...
WHERE p.id IN (productIDs)
And the code to get product ids is something like this:
public function getOrderData($data) {
$dataArray = json_decode($data, true);
$data = json_decode($data);
$arrayKeys = array_keys($dataArray);
$arrayKeys = array_filter($arrayKeys, function($value) {
return ($value !== 'shipping');
});
$productIds = implode(',', $arrayKeys);
// QUERY FOR selecting product categories, so when this query return results you can merge them with order data.
$productsInfo = ....
foreach ($productsInfo as $item) {
if(!in_array($item['productId'], $arrayKeys)) continue;
$dataArray[$item['product_id']]['category'] = $item['category_name']; // depending on the implementation $item may be array or an object! so this code may vary
$dataArray[$item['product_id']]['subcategory'] = $item['sub_cat_name '];
}
// If products is array of objects use this code
//foreach ($productsInfo as $item) {
// if(!in_array($item->productId, $arrayKeys)) continue;
// $dataArray[$item->product_id]['category'] = $item->category_name; // depending on the implementation $item may be array or an object! so this code may vary
// $dataArray[$item->product_id]['subcategory'] = $item->sub_cat_name ;
//}
return (object) $dataArray;
}
If return (object) $dataArray; doesn't give you the same response structure as in the response in your question then:
return json_decode(json_encode($dataArray)); // for example
If you add additional info about table structure I can give you better answer.
Try this ;)
When you do json_decode it converts associative array to objects that's why you are getting this error when passing this decoded json data; So before using this decoded data cast it to array;
$data = (array) json_decode($data);

add new element in laravel collection object

I want to add new element in $items array, I don't want to use joins for certain reasons.
$items = DB::select(DB::raw('SELECT * FROM items WHERE items.id = '.$id.' ;'));
foreach($items as $item){
$product = DB::select(DB::raw(' select * from product
where product_id = '. $id.';' ));
$item->push($product);
}
What should I do?
It looks like you have everything correct according to Laravel docs, but you have a typo
$item->push($product);
Should be
$items->push($product);
push method appends an item to the end of the collection:
I also want to think the actual method you're looking for is put
$items->put('products', $product);
put method sets the given key and value in the collection
As mentioned above if you wish to add as a new element your queried collection you can use:
$items = DB::select(DB::raw('SELECT * FROM items WHERE items.id = '.$id.' ;'));
foreach($items as $item){
$product = DB::select(DB::raw(' select * from product
where product_id = '. $id.';' ));
$items->push($product);
// or
// $items->put('products', $product);
}
but if you wish to add new element to each queried element you need to do like:
$items = DB::select(DB::raw('SELECT * FROM items WHERE items.id = '.$id.' ;'));
foreach($items as $item){
$product = DB::select(DB::raw(' select * from product
where product_id = '. $id.';' ));
$item->add_whatever_element_you_want = $product;
}
add_whatever_element_you_want can be whatever you wish that your element is named (like product for example).
If you want to add item to the beginning of the collection you can use prepend:
$item->prepend($product, 'key');
If you want to add a product into the array you can use:
$item['product'] = $product;
I have solved this if you are using array called for 2 tables. Example you have,
$tableA['yellow'] and $tableA['blue'] . You are getting these 2 values and you want to add another element inside them to separate them by their type.
foreach ($tableA['yellow'] as $value) {
$value->type = 'YELLOW'; //you are adding new element named 'type'
}
foreach ($tableA['blue'] as $value) {
$value->type = 'BLUE'; //you are adding new element named 'type'
}
So, both of the tables value will have new element called type.
This is what i would do...
$items = Item::find($id);
foreach($items as $item){
$product = Product::find($id);
$item->product = $product;
}
This would assign $product to each $item

Categories