Laravel 5 collection issue: Where not equal to - php

I am currently working on a modal where a user can insert an excel file. The task of the system is to upload and/or add a new database record if the records are new or identical to what exists in the database. BUT it also needs a delete function for getting rid of those records where the slug column is not identical to the name column.
At the moment I am using Laravel 5.3, and this is my controller as it is now:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
use App\Http\Requests;
use Illuminate\Support\Facades\DB;
use Input;
use Maatwebsite\Excel\Facades\Excel;
class ProductsController extends Controller {
public function importExcel(Request $request) {
if (Input::hasFile('productFile')) {
$path = Input::file('productFile')->getRealPath();
$checkbox = Input::get('productCheckbox');
$data = Excel::load($path, function($reader) {
})->get();
if (!empty($data) && $data->count()) {
foreach ($data as $key => $value) {
$product = Product::all()->where('slug', $value->slug)->first();
$product_false = Product::all()->where('slug', '!=' , 'name')->get();
if ($product_false !== null){
//delete row if slug does not matches name
dd($product_false);
}
The dd above returns all products, so the collection query is not working properly (see below for the raw SQL that I am trying to run in this collection)
if ($product !== null) {
//update row if exist
$product->name = $value->name;
$product->description = $value->description;
$product->price = $value->price;
$product->save();
} else {
//add new row if not exist
$product = new Product;
$product->slug = $value->slug;
$product->name = $value->name;
$product->description = $value->description;
$product->price = $value->price;
$product->save();
}
}
header("Location: /products");
}
}
}
}
This is the Product model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'slug', 'name', 'description', 'price',
];
}
Here is the PHPMyAdmin raw SQL (which works) that I basically am looking for to use in the collection:
SELECT * FROM `products` WHERE `slug` != `name`
I hope someone can help me out from this pit. I have been sailing the waves of the internet for about 12 hours now just to get this done.
~ nitsuJ

Collections, eloquent and query builder are not the same. Collection provide a bunch of methods to work on arrays, rather then on the database or model.
In collection context whereNot() is not available.
but the same function can be achieved through whereNotIn('key', [value])
collect([
[
'name' => 'foo',
'rank' => 2
],[
'name' => 'bar',
'rank' => 3
],[
'name' => 'foobar',
'rank' => 4
],
])->whereNotIn('rank', [4])
same as where rank not in (4)

Change
$product = Product::all()->where('slug', $value->slug)->first();
$product_false = Product::all()->where('slug', '!=' , 'name')->get();
Into
$product = Product::where('slug', $value->slug)->first();
$product_false = Product::where('slug', '!=' , 'name')->get();

Try this
$product = Product::where('slug', $value->slug)->first();
$product_false = Product::whereRaw('slug != name')->get();
Simple where won't work as it compares products.slug with "name"(string).

I managed to solve it.
$data = Excel::load($path, function($reader) {
$importedSlugs = $data->select(array('slug'))->toArray();
//collection of imported slugs
$collectionOfImportedSlugs = collect($importedSlugs)->flatten()->all();
//get all product slugs
$productSlugs = Product::all()->pluck('slug');
//get all different slugs!
$diffSlugsArray = $productSlugs->diff($collectionOfImportedSlugs)->all();
//dd($diffSlugsArray);
foreach ($diffSlugsArray as $diffSlug) {
$product_false = Product::all()->where('slug', $diffSlug)->first();
echo $product_false->slug . 'has been deleted!';
$product_false->delete();
}
})->get();

Related

Pass id from first model into a Eloquent

I have a problem wanting to pass the id of Products in the subqueries.
The first code is what I have so far. The second is the way I want to do with Eloquent, but I can't.
$result = [];
Product::with(['locals.presentations'])->each(function ($product) use (&$result) {
$body['id'] = $product->id;
$body['nombre'] = $product->nombre;
$sedes = [];
$product->locals->each(function ($local) use (&$sedes, $product) {
$presentations = [];
$local->presentations->each(function ($presentation) use (&$presentations, $local, $product) {
if ($presentation->local_id == $local->id && $presentation->product_id == $product->id) {
$presentations[] = [
'local_id' => $presentation->local_id,
'product_id' => $presentation->product_id,
'presentacion' => $presentation->presentation,
'precio_default' => $presentation->price
];
}
});
...
});
return $result;
I want transform the previous code into this with Eloquent, but I can't pass the product_id into the subqueries:
$products = Product::with(['locals' => function ($locals) {
//How to get the id from Product to pass in the $presentations query ??????
$locals->select('locals.id', 'descripcion')
->with(['presentations' => function ($presentations) {
$presentations
// ->where('presentations.product_id', $product_id?????)
->select(
'presentations.local_id',
'presentations.product_id',
'presentations.id',
'presentation',
'price'
);
}]);
}])->select('products.id', 'nombre')->get();
return $products;
Product
public function locals()
{
return $this->belongsToMany(Local::class)->using(LocalProduct::class)
->withPivot(['id', 'is_active'])
->withTimestamps();
}
Local
public function presentations()
{
return $this->hasManyThrough(
Presentation::class,
LocalProduct::class,
'local_id',
'local_product_id'
);
}
You can simply use the has() method if you have set the relations correctly on the Product and Local models. This will return ONLY the products which has locals AND presentations.
If you want every product but only the locals and presentations with the product_id equals to the products.id, then you don't have to do anything. The relationship you set in your models already checks if the id matches.
$products = Product::has('locals.presentations')
->with(['locals' => function ($locals) {
$locals
->select('locals.id', 'descripcion')
->with(['presentations' => function ($presentations) {
$presentations->select(
'presentations.local_id',
'presentations.product_id',
'presentations.id',
'presentation',
'price'
);
}]);
}])->select('products.id', 'nombre')->get();

Checking duplicated data in Laravel

This code work as send to inn_db table from ext_db.
but it cannot check if the data is the same or different in inn_db.
So there posited same values in inn_db.
How could I add that job?.
Laravel-5.4, MySQL, InnoDB.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use \DB;
class UpdateCustomerController extends Controller
{
public function db_update()
{
$customers = \DB::connection('ext_db')->table('customers')->orderBy('customer_id')->chunk(1000, function ($all){
foreach ($all as $kunde){
DB::connection('inn_db')->table('custoemrs')->insert(
[$kunde->customer_id
$kunde->name,
$kunde->email]);
}
});
}
}
Finally, I got the answer as below code with connect view when after discussion.
Thanks to #Pramid and #Badea :)
$customers = \DB::connection('ext_db')->table('customers')->orderBy('customer_id')->chunk(1000, function ($all){
foreach ($all as $kunde){
$existing_kunde = DB::connection('inn_db')->table('customers')->where([
['customer_id', '=', $kunde->customer_id],
['name', '=', $kunde->name],
['email', '=', $kunde->email]
])->first();
if ( ! $existing_kunde) {
DB::connection('inn_db')->table('customers')->insert([
'customer_id' => $kunde->customer_id,
'name', => $kunde->name,
'email', => $kunde->email
]);
}
}
});
$kundes = \DB::connection('ext_db')->table('customers')->get();
return view('kundes.index')
->with('kundes', $kundes);
Give this a try, you basically need to check each record of the chunk in your customer table if it doesn't exist then allow them to get insert into customer table
public function db_update()
{
$customers = \DB::connection( 'ext_db' )->table( 'customers' )->orderBy( 'customer_id' )->chunk( 1000, function ( $all ) {
foreach ( $all as $kunde ) {
$kunde_exist = DB::connection( 'inn_db' )->table( 'customers' )
->where( [
'customer_id' => $kunde->customer_id,
'name' => $kunde->name,
'email' => $kunde->email,
] )->first();
if ( ! $kunde_exists ) {
DB::connection( 'inn_db' )->table( 'customers' )->insert(
[ $kunde->customer_id
$kunde->name,
$kunde->email]);
}
}
} );
}
Yes, you need to check before inserting a customer. Just add a condition like bellow:
foreach ($all as $kunde){
$existing_kunde = DB::connection('inn_db')->table('custoemrs')->where('customer_id', $kunde->customer_id)->first();
if ( ! $existing_kunde) {
DB::connection('inn_db')->table('custoemrs')->insert(
[$kunde->customer_id
$kunde->name,
$kunde->email]);
}
}

groupBy only gets the first id

I have a many to many relationship between departments and users my pivot table is department_user. I wanted to select all the department_name depending of the user's department using groupBy method to merge all the department_name into one. See below my statement.
$departmentRecipient = DB::table('users')->select('departments.department_name', 'users.id')
->join('department_user', 'users.id', '=', 'department_user.user_id')
->join('departments', 'department_user.department_id', '=', 'departments.id')
->groupBy('departments.department_name')
->get();
Result using die and dump.
As you can see here I have an id of 4 under "Department of Engineering". My main problem is it doesn't fetch all the id under "Department of Engineering". But in my SQL I have id of 5 not only 4. How can I solve this problem? Any help would greatly appreciated. Please see result below.
Output:
This is the output of my list. I wanted to get all the users id belongs to the specific option for the user. But if I choose "Department of Engineering" it only gets the id of 4 not 5. I wanted to get 4 and 5 once.
Controlller:
public function getDocuments()
{
$departmentRecipient = DB::table('departments')->get();
return view ('document.create')->with('departmentRecipient', $departmentRecipient);
}
public function postDocuments(Request $request)
{
$this->validate($request,
[
'title' => 'required|regex:/(^[A-Za-z0-9 ]+$)+/|max:255',
'content' => 'required',
'category' => 'required',
'recipient' => 'required',
]);
$document = new Document();
//Request in the form
$document->title = $request->title;
$document->content = $request->content;
$document->category_id = $request->category;
$document->save();
$user = Auth::user();
foreach($request->recipient as $recipientId)
{
$document->sentToUsers()->sync([ $recipientId => ['sender_id' => $user->id]],false );
}
}
Model
User
public function departments()
{
return $this->belongsToMany('App\Models\Department', 'department_user');
}
Department
public function users()
{
return $this->belongsToMany('\App\Models\User', 'department_user');
}
View
<div class = "form-group">
<label for = "recipient" class = "control-label">Recipient:</label>
<select name = "recipient[]" multiple class = "form-control select2-multi" id = "myUserList">
#foreach ($departmentRecipient as $list)
<option value = "{{ $list->id }}">{{ $list->department_name }}</option>
#endforeach
</select>
</div>
From your given code it seems you are not using Eloquent ORM, you are doing it using Query Builder.
If you don't have a performance concern right now you can do it using separate queries. Like-
$departmentRecipient = DB::table('departments')->all();
foreach($departmentRecipient as $department){
$department->users = DB::table('department_user')->where('department_id',$department->id)->pluck('user_id');
}
But the better way is to use the eloquent with relationships. Define the many to many relationship in your eloquent model of Users and Departments (assuming you have eloquent model for them). You will find details about eloquent relationships at laravel documentation.
Update:
From the update of your post it is actually pretty easy to do what you want. If your Request contains the selected department id then you can do the following:
public function postDocuments(Request $request)
{
$document = new Document();
$document->title = $request->title;
$document->content = $request->content;
$document->category_id = $request->category;
$document->save();
//get the users list of the selected department id
$selected_department = $request->department_id; //this must be included in your POST data
$users = DB::table('department_user')->where('department_id',$selected_department)->pluck('user_id');
//now you have the list of the users id
foreach($users as $user){
// do what you need here
}
}
Update 2:
Following controller code might work for you.
Controller:
public function getDocuments()
{
// I am suggesting here to change the '$departmentRecipient' to '$departmentlist', as it is more meaningful. Also in your view
$departmentlist = DB::table('departments')->get();
return view ('document.create')->with('departmentlist', $departmentlist);
}
public function postDocuments(Request $request)
{
//same as you have till the $document->save()
....
....
//then do this
$recipients = array();
$departments = $request->recipient;
foreach($departments as $department){
$users = DB::table('department_user')->where('department_id',$department)->pluck('user_id');
$recipients = array_merge($recipients, $users);
}
//now you have a complete list of the users from selected departments
//Do as you intend to like this
$user = Auth::user();
foreach($recipients as $recipientId)
{
$document->sentToUsers()->sync([ $recipientId => ['sender_id' => $user->id]],false );
}
}

Inserting array using Laravel Eloquent

I have an array which I combined using array_combine. I am trying to store each array KEY AND VALUE into a child table. However, I can't figure out to do this. Please help!
HERE'S AN EXAMPLE OF A RETURNED ARRAY
array:2 [▼
"Design" => "Pattern"
"Brand" => "Sony"
]
PRODUCT MODEL
public function productAttributes()
{
return $this->hasMany('App\ProductAttribute');
}
PRODUCT ATTRIBUTE MODEL
protected $fillable = [
'attribute_name', 'attribute_value', 'used_as_filter'
];
public function product()
{
return $this->belongsTo('App\Product');
}
PRODUCT CONTROLLER
$product = new Product();
$product->category_id = $request->category_list;
$product->name = $request->name;
$product->price = $request->price;
$product->save();
/**Optional Data**/
if ($request->has('attribute_name')){
$attributes = array_combine($request->input('attribute_name'), $request->input('attribute_value'));
$product->productAttributes()->create($attributes);
}
When I run this, I get one row inserted into the product table, and one row inserted into the product_attribute table. However, the columns attribute_name and attribute_value is blank.
$attributes = array_combine($request->input('attribute_name'), $request->input('attribute_value'));
collect($attributes)->each(function ($value, $name) use ($product) {
$product->productAttributes()->create([ 'attribute_name' => $name, 'attribute_value' => $value, ]);
});

Yii2 checkboxlist broken down into categories (nested sets)

I seem to be having some trouble creating a form input that allows checkboxlists and nested sets to work together.
What I'd like, is something exactly like what bookbub does:
http://i.imgur.com/PfpgSf5.jpg
Right now in my database I have it structured as follows:
Category table
- id
- name
- parent_id
Basically, my idea is to display everything on the _form that has parent_id as null as a heading (no checkbox) and everything that has a parent_id as a checkbox under the appropriate heading.
However, the only solution that I can get that's close doesn't seem to allow me to have checkboxes already checked if we're updating a user's preferences. It does however display things exactly how I would like. Here's what I have so far:
ProfileReader's (ProfileReader is my model that extends users to hold their preferences) _form:
<?php
$primaryCategories = (new Category)->getPrimaryCategories();
//$selectedCategories = yii\helpers\ArrayHelper::map($model->categories, 'id', 'name');
foreach ($primaryCategories as $pc) {
echo '<p>'.$pc->name.'</p>';
if ($pc->subCategories) {
//the following never fully worked. It doesn't automatically check the boxes for relations that
//are already setup. You need to somehow use $model->categories[#] and 'id' for that to work
//echo $form->field($model->categories[#], 'id')->label(false)
echo $form->field($pc, 'subCategories[' . $pc->id . '][]')->label(false)
->checkboxList(yii\helpers\ArrayHelper::map($pc->subCategories, 'id', 'name'),
['separator' => '<p>']);
}
}
?>
ProfileReaderController:
public function actionUpdate()
{
$model = $this->findModel(\Yii::$app->user->identity->id);
if ($model == null) {
$model = new ProfileReader();
$model->user_id = \Yii::$app->user->identity->id;
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
//link the categories to the pen name
$categories = Yii::$app->request->post()['Category']['subCategories'];
$model->unlinkAll('categories', true);
foreach ($categories as $category) {
if ($category)
foreach ($category as $c) {
$c = (new Category)->findOne($c);
$model->link('categories', $c);
}
}
return $this->redirect(['update']);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
ProfileReader:
public function getCategories()
{
return $this->hasMany(Category::className(), ['id' => 'category_id'])
->viaTable('user_category', ['user_id' => 'user_id']);
}
Does anyone have any clue how I can make this work? Is it even possible in Yii2 with activeform?
Okay, after many hours I finally figured it out. Posting my resulting code here so that it may help someone else. Don't ask me to explain it as I don't fully get it myself :P
It might also be a good idea to do some testing on it too before throwing it into a live environment, I haven't done any yet.
update action:
/**
* Updates an existing ProfileReader model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate()
{
$model = $this->findModel(\Yii::$app->user->identity->id);
if ($model == null) {
$model = new ProfileReader();
$model->user_id = \Yii::$app->user->identity->id;
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
//unlink the categories first to avoid duplicates
$model->unlinkAll('categories', true);
//link the categories to the pen name
foreach ($model->categoriesArray as $pc) {
if ($pc) {
foreach ($pc as $sc) {
$sc = (new Category)->findOne($sc);
$model->link('categories', $sc);
}
}
}
return $this->redirect(['update']);
} else {
//get the categories and separate them into groups based on parent_id
foreach ($model->categories as $c) {
$model->categoriesArray[$c->parent_id][] = $c;
}
return $this->render('update', [
'model' => $model,
]);
}
}
ProfileReader model (had to add a variable):
public $categoriesArray;
_form:
<label class="control-label">Categories</label>
<?php
$allCategories = (new Category)->getOrderedCategories();
foreach ($allCategories as $pc) {
echo '<p>'.$pc['name'].'</p>';
echo $form->field($model, 'categoriesArray['.$pc['id'].'][]')->label(false)
->checkboxList(yii\helpers\ArrayHelper::map($pc['subCategories'], 'id', 'name'),
['separator' => '<p>']);
}
?>

Categories