Laravel 8 how to implement whereNot - eloquent

In Laravel 9 you have a new method called whereNot but not in Laravel 8. I cannot upgrade to Laravel 9 right now so I am looking for an alternative solution.
The goal is to exclude products that both have zero in stock and stock_date older than one month. The trick is both conditions should be considered or they are disregarded. So a product can have zero stock and less than one month old and still be part of the result.
Here is how I would probably do it in Laravel 9:
$query = Product::select('id')
->where('status', 1)
->where('type', 1)
->whereNot(function ($q) {
$q->where('stock', 0)
->where('stock_date', '<', Carbon::now()->subMonths(1));
});
I would probably write this in pure MySQL:
# The assumption is that the current date is: 2022-10-31
SELECT
id
FROM
products
WHERE
status = 1
AND type = 1
AND NOT (stock = 0 AND stock_date < '2022-09-31')

Well, if you cannot use new features, which is understandable, then you should invert your query like so:
$query = Product::select('id')
->where('status', 1)
->where('type', 1)
->where(function ($q) {
$q->where('stock', '<>', 0)
->where('stock_date', '>=', Carbon::now()->subMonths(1));
})
->get();

Related

Magento 2 - Category List Sort by Position with Sub Category Not Working

A little background, I'm trying to do a custom Category listing, but at the moment it seems the Category not being sort as I seen on Admin.
Here's the code that I've done so far
$current_store = $this->_storeManager->getStore();
$root_category_id = $this->_storeManager->getStore()->getRootCategoryId();
$collection = $this->_categoryCollectionFactory->create()
->addAttributeToSelect('*')
->addAttributeToFilter('is_active', 1)
->setStore($current_store);
return $collection->addAttributeToFilter('entity_id', array('nin' => $root_category_id))
->setOrder('position','ASC');
And the result, when I tried to echo its ID is like below
3
10
4
11
5
7
12
8
15
9
13
14
16
6
But, from the Admin, it doesn't reflect the order correctly, below is the figure
The problem that I realize is, that, I have sub category, I tried to echo the query from above code, and then copy-paste it into sql GUI, and I realize, the position is kinda weird but, it does make-sense, because it's a sub category.
Here's the result when I execute the query on sql GUI
So, what I tried to achieve is to sort above result, to reflecting what I set on Admin.
Is there anything that I missed? I'm not sure where to look, since I've been stuck around 1-2 days, not sure what's the proper keyword, almost all keyword I did will arrive to product sort or kind of that, not category sort
Thanks in Advance!
For those who still needs some answer relating to this question, here's the answer
...
$store_categories = $this->_categoryFactory->create();
$store_categories = $store_categories->load($this->_root_category_id)->getChildrenCategories();
foreach ($store_categories as $category) {
//get id
$category_id = $category->getId();
//get category model
$category = $this->getCategoryModel($category_id);
$sub_children = $this->getActiveChildCategories($category);
if (count($sub_children) > 0) {
$sub_categories = $this->getSubCategory($sub_children);
$categories = array_merge($categories, $sub_categories);
} else {
$categories[] = $category;
}
}
...
The _categoryFactory comes from Magento\Catalog\Model\CategoryFactory.
It's pretty much covering what I want, but not really as I expected before, because I think it's not really efficient.
PS - I'm still new on Magento 2, so, if someone else has other answer that might be pretty much like I expect, then, I'm happily change it as Accepted Answer. :)
Goodluck!

Script to update 5500 fields in a telephone type column with random numbers

I would need to create a php7 script that generates 5500 random telephone numbers starting with the example number 3
"3471239900". The script should go to overwrite the data already present.
/**
* genera numero tel casuale che inizia per 3
*/
function telefono()
{
$telefono = '';
for ($k=0; $k<9; $k++) {
//genera casuale 9 cifre
$telefono .= rand(0, 9);
}
//inizia per 3
return '3' . $telefono;
}
$res = mysqli_query($conn, 'SELECT id_com FROM commesse ORDER BY id_com');
while ($riga = mysqli_fetch_assoc($res)) {
$id = (int)$riga['id_com'];
$query = "UPDATE commesse SET cliente=tel='".telefono()."' WHERE id_com=" . $id_com;
}
You don't need to invent such code to fill a single column in a database table with random numbers.
Following update statement will populate cliente_tel column of the commesse table with 10 digit random numbers all beginning with 3.
UPDATE
`commesse`
SET
`cliente_tel` = CONCAT("3",ROUND(RAND()*(999999999-100000000)+100000000))
WHERE 1;
Using ROUND() is necessary here since RAND() returns a float between 0 and 1.
Good to remember: Running any kind of update/insert statement in a loop is always expensive and slow. Try to avoid running SQL queries in a loop as much as possible.

Within Where Clause, Nested Case When Statement to return Result with 'OR'

Good afternoon everyone,
I am trying to combine 2 separate function to create a semi-dynamic where clause. Currently I have 3 identical view in my SQL database which are pretty much the same except for the following line pending on what the user state are.
For WA User.
(dbo.JunctionT.ProcessState = 'CDS' )
For VIC User.
(dbo.JunctionT.ProcessState = 'VIC' OR dbo.JunctionT.ProcessState = 'WA')
For NSW User.
(dbo.JunctionT.ProcessState = 'NSW')
Therefore, if the user is from NSW, return results from their user state.. and if the user is from WA then return result where ProcessState is CDS and if the user is from VIC then return result where ProcessState is either VIC or WA.
I have written the following nested case when statement to try and combine these 3 views into 1:
`dbo.JunctionT.ProcessState =
(CASE
WHEN dbo.fnGetReviewState(CURRENT_USER) = 'NSW' THEN 'NSW'
WHEN dbo.fnGetReviewState(CURRENT_USER) = 'WA' THEN 'CDS'
WHEN dbo.fnGetReviewState(CURRENT_USER) = 'VIC' THEN 'VIC OR WA'
END)`
This seems to be working perfectly fine for both NSW and WA users but when I trial it as a VIC user, it returns nothing. I suspect it could be a syntax issue but i have tried the following without much success:
Have tried to use:('VIC OR WA'), ('VIC' OR 'WA'), ['VIC' OR 'WA'], <'VIC' OR 'WA'>
Hoping that someone more knowledgeable is able to show me what it is I am missing or even suggest a better way to complete this dynamic statement. Many many thanks in advance!!
SeanY
Brien is close. This should do the trick:
case
when dbo.JunctionT.ProcessState = 'NSW' and
dbo.fnGetReviewState(CURRENT_USER) = 'NSW' then 1
when dbo.JunctionT.ProcessState = 'CDS' and
dbo.fnGetReviewState(CURRENT_USER) = 'WA' then 1
when dbo.JunctionT.ProcessState in ( 'VIC', 'WA' ) and
dbo.fnGetReviewState(CURRENT_USER) = 'VIC' then 1
else 0
end = 1
'VIC OR WA' is literally "VIC OR WA", that is why there are no rows returning.
dbo.JunctionT.ProcessState would have to equal "VIC OR WA" (this exact/literal string) to return rows.
What you want instead dbo.JunctionT.ProcessState IN ('VIC','WA')
So there is an element of dynamic SQL involved in order to have your CASE statement return exactly what you need.

CakePHP 3 - Comparing and modifying dates

In the CakePHP 3 Cookbook on Date/Time, you can compare time intervals with future/past days/weeks using IsWithinNext/WasWithinNext. You can also modify dates/times by doing a ->modify('extra time') - eg. if $date = 2016-01-01, $date->modify('+1 week') would mean $date = 2016-01-08.
These features require the use of Cake\i18n\Time. However, when I attempted to use these features, I received a Cake error:
Call to a member function isWithinNext() on string.
This is the code I used:
$date_start = \Cake\Database\Type::build('date')->marshal($data['session']['date_start'])->i18nFormat(); //before hand my dates were in the form of an array comprised of Year, Month and Day. This changes them into date format.
if($date_start->isWithinNext('1 week')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+48 hours');
} elseif ($date_start->isWithinNext('2 weeks')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+1 week');
} elseif ($date_start->isWithinNext('3 weeks')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+1 week');
} else {
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+2 weeks');
}
Calling i18nFormat() returns a formatted string as you can look up in the API: https://api.cakephp.org/3.4/class-Cake.I18n.DateFormatTrait.html#_i18nFormat
This, for example, should work:
$date_start = new \Cake\I18n\Time($data['session']['date_start']);
debug($date_start->isWithinNext('2 weeks'));

How can I filter a Perl DBIx recordset with 2 conditions on the same column?

I'm getting my feet wet in DBIx::Class — loving it so far.
One problem I am running into is that I want to query records, filtering out records that aren't in a certain date range.
It took me a while to find out how to do a <= type of match instead of an equality match:
my $start_criteria = ">= $start_date";
my $end_criteria = "<= $end_date";
my $result = $schema->resultset('MyTable')->search(
{
'status_date' => \$start_criteria,
'status_date' => \$end_criteria,
});
The obvious problem with this is that since the filters are in a hash, I am overwriting the value for "status_date", and am only searching where the status_date <= $end_date. The SQL that gets executed is:
SELECT me.* from MyTable me where status_date <= '9999-12-31'
I've searched CPAN, Google and SO and haven't been able to figure out how to apply 2 conditions to the same column. All documentation I've been able to find shows how to filter on more than 1 column, but not 2 conditions on the same column.
I'm sure I'm missing something obvious. Can someone here point it out to me?
IIRC, you should be able to pass an array reference of multiple search conditions (each in its own hashref.) For example:
my $result = $schema->resultset('MyTable')->search(
[ { 'status_date' => \$start_criteria },
{ 'status_date' => \$end_criteria },
]
);
Edit: Oops, nervermind. That does an OR, as opposed to an AND.
It looks like the right way to do it is to supply a hashref for a single status_date:
my $result = $schema->resultset('MyTable')->search(
{ status_date => { '>=' => $start_date, '<=' => $end_date } }
);
This stuff is documented in SQL::Abstract, which DBIC uses under the hood.
There is BETWEEN in SQL and in DBIx::Class it's supported:
my $result = $schema->resultset('MyTable')
->search({status_date => {between => [$start_date,$end_date]}});