I have this route defined:
Route::get('/categories/{category}/{country}/{state?}/{county?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
public function show(Category $category, Country $country, State $state = null, County $county = null, City $city = null) {
echo 'ok';
}
It's fine, automatically checks for relationships, works with 1, 2 or 3 of optional parameters. But... I want to extend it so it that the COUNTY is not always mandatory. Cause there are some cities that have direct relationship to state without county_id in the middle. Cities table has county_id and also state_id and always only one of them is present. If I add:
Route::get('/categories/{category}/{country}/{state?}/{city?}', ['App\Http\Controllers\LocationController', 'show'])->withScopedBindings();
Only one of the routes are working.
How can I fix this? Thanks.
You can define two separate routes
Route::get(
'/categories/{category}/{country}/{state?}/{county?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county');
Route::get(
'/categories/{category}/{country}/{state?}/{county?}/{city?}',
['App\Http\Controllers\LocationController', 'show']
)
->withScopedBindings()
->name('categories.show.county.city');
And then check for the route name in the controller
public function show(
Category $category,
Country $country,
State $state = null
) {
if(
request()->route()->named('categories.show.county') &&
request()->route()->hasParameter('county')
) {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->named('categories.show.county.city')) {
if(request()->route()->hasParameter('county') {
$county = County::where(
(new County)->getRouteKeyName(),request()->route('county')
)
->firstOrFail();
}
if(request()->route()->hasParameter('city')) {
$city = City::where(
(new City)->getRouteKeyName(),request()->route('city')
)
->firstOrFail();
}
}
}
So, following Donkarnash answer I finally have a solution.
Route::get('/categories/{category}/{country}/{state?}/{county_slug?}/{city_slug?}',
['App\Http\Controllers\LocationController', 'show'])->scopeBindings();
public function show(
Category $category,
Country $country,
State $state = null,
$county_slug = null,
$city_slug = null
) {
if ($county_slug && $city_slug)
{
// two parameters present, that means the chain is state -> county -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->firstOrFail();
$city = City::where('county_id', $county->id)
->where('slug', $city_slug)
->firstOrFail();
} else {
if ($county_slug) {
// one parameter present, that means the chain is state -> county OR state -> city
$county = County::where('state_id', $state->id)
->where('slug', $county_slug)
->first();
if (!$county) {
$city_slug = $county_slug;
$city = City::where('state_id', $state->id)
->where('slug', $city_slug)
->first();
}
if (!$county && !$city) {
abort(404);
}
}
}
}
And then in migrations states table:
$table->unique(['slug', 'country_id']);
Counties table:
$table->unique(['slug', 'state_id']);
Cities table:
$table->unique(['slug', 'state_id']);
$table->unique(['slug', 'county_id']);
And it works. Only drawback is that if there is a county and a city with the same slug that belong to the same state. For example, county with slug "test" and state_id "15" and city with slug "test" and state_id "15". Then it won't work correctly and result as county. But usually ALL cities for a country have a chain of country -> state -> county -> city OR country -> state -> city, so this minor drawback won't affect final results for the website. Nevertheless, this also can be fixed by adjusting request validation rules.
Related
I am new in cakephp and my table like:
city
id | name
1 | city1
2 | city2
state
id | name | cityid
1 |state1| 2
so how do i get the city name if i having state id.
In controller i have code like this.
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$sId= $this->request->data['stateid'];
}
}
In the $sId i get value so how do i write query.
If you have a BelongsTo relationship between both Model, you just have to do a query on the States which contains City:
public function getCity()
{
if( $this->request->is('ajax') ) {
$this->autoRender = false;
}
if ($this->request->isPost()) {
$stateEntity = $this->States->find('all')
->where(['id' => $this->request->data['stateid']])
->contain(['Cities'])
->first();
// Now the State Object contains City
$cityName = $stateEntity->city->name;
}
}
To create this relationship you need to do like this:
class StatesTable extends Table
{
public function initialize(array $config)
{
$this->belongsTo('Cities')
->setForeignKey('city_id')
->setJoinType('INNER');
}
}
I'm using MySQL and i have schema like:
|------------|-------------|------------|--------------|
| cities |category_city| categories| companies |
|------------|-------------|------------|--------------|
| id | city_id | id | id |
| name | category_id | name |subcategory_id|
| | | parent_id | city_id |
| | | |...other cols |
|____________|_____________|____________|______________|
Relationships:
City with Category has ->belongsToMany()
public function categories()
{
return $this->belongsToMany(Category::class);
}
Categories has subcategories:
public function subcategories()
{
return $this->hasMany(Category::class, 'parent_id', 'id');
}
And i'm getting companies from category and filtering by city, because i need the current city companies and for that i have a global scope:
public function getCompanies($city_id)
{
return $this->companies()->whereHas('mainCity', function ($q) use ($city_id) {
$q->where('city_id', $city_id);
});
}
mainCity method:
public function mainCity()
{
return $this->belongsTo(City::class, 'city_id');
}
Here is my method performing the query with AJAX request:
public function getPlaces(City $city, Category $subcategory, $north, $south, $east, $west)
{
$companies = $subcategory->companies()
->withCount('comments')
->companiesByBounds($north, $south, $east, $west)
->paginate(8);
$markers = $subcategory->companies()
->companiesByBounds($north, $south, $east, $west)
->get(['lat', 'lng', 'slug', 'title']);
return response()->json(['companies' => $companies, 'markers' => $markers], 200, [], JSON_NUMERIC_CHECK);
}
and by companiesByBounds scope method:
public function scopeCompaniesByBounds($query, $north, $south, $east, $west)
{
return $query->whereBetween('lat', [$south, $north])
->whereBetween('lng', [$west, $east]);
}
In companies i have ~2m records. The main problem is that the queries taking 3.5 seconds. Help please to improve my queries.
Here is the query:
select count(*) as aggregate from `companies` where `companies`.`category_id` = '40' and `companies`.`category_id` is not null and `lat` between '53.68540097020851' and '53.749703253622705' and `lng` between '91.34262820463869' and '91.51600619536134'
To improve speed you need to add indexes on the columns lat and lng.
CREATE INDEX idx_lat ON companies (lat);
The indexes are used in queries when the columns are added to conditions.
What is the correct/best way to update a record within a record?
The following attempt:
type alias Model =
{ pageView : PageView
, landingPageModel : Dict
}
--update
update : Action -> Model -> Model
update action model =
case action of
ChangePage pView ->
{ model | pageView = pView }
PostCode pCode ->
let
lPModel =
model.landingPageModel
newlPModel =
{ lPModel | postCode = pCode }
in
{ model | landingPageModel = newlPModel }
gave this error:
The type annotation for `update` does not match its definition.
19│ update : Action -> Model -> Model
^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:
Action
-> { ..., landingPageModel : Dict }
-> { ..., landingPageModel : Dict }
But I am inferring that the definition has this type:
Action
-> { ..., landingPageModel : { a | postCode : String } }
-> { ..., landingPageModel : { a | postCode : String } }
This is somewhat surprising - isn't a literal Dict update of type Dict?
I simply needed to expand the Model definition :
type alias Model =
{ pageView : PageView
, landingPageModel : { postCode : String }
}
Also, as per halfzebra's comment, Dict's have no relevance here - just records.
So I have two arrays that I need to update and set with in MySQL. item_id [1,2,3] and item_order[2,1,3]
Here is the items table before the array insert:
item_id item_order
1 1 //should become 2
2 2 // should become 1
3 3 // should become 3
The arrays should be in pairs for the insert, 1-2, 2-1, 3-3.
How can I do this with a prepared statement efficiently and how can I test if the array items are indeed numbers?
Assuming you have input like this:
$item_id = array(1, 2, 3);
$item_order = array(2, 1, 3);
// and a PDO connection named $pdo
You can try something like this. (I'm also assuming you have PDO configured to throw exceptions when problems arise).
function all_numbers($input) {
foreach($input as $o) {
if(!is_numeric($o)) {
return false;
}
}
return true;
}
if(count($item_id) != count($item_order)) {
throw new Exception("Input size mismatch!");
}
if(!all_numbers($item_id) || !all_numbers($item_order)) {
throw new Exception("Invalid input format!");
}
$pairs = array_combine($item_id, $item_order);
// now $pairs will be an array(1 => 2, 2 => 1, 3 => 3);
if($pdo->beginTransaction()) {
try {
$stmt = $pdo->prepare('UPDATE `items` SET `item_order` = :order WHERE `item_id` = :id');
foreach($pairs as $id => $order) {
$stmt->execute(array(
':id' => $id,
':order' => $order,
));
}
$pdo->commit();
} catch (Exception $E) {
$tx->rollback();
throw $E;
}
} else {
throw new Exception("PDO transaction failed: " . print_r($pdo->errorInfo(), true));
}
But it might be better to redesign your input - only pass the item_ids in the desired order and compute their item_order values automatically.
Here is an example:
UPDATE mytable
SET myfield = CASE other_field
WHEN 1 THEN 'value'
WHEN 2 THEN 'value'
WHEN 3 THEN 'value'
END
WHERE id IN (1,2,3)
I'm trying to move the following query to Linq-to-sql, is it possible?
select * from (
Select top (#Percent) percent with ties *
from(
Select distinct
LoanNumber as LoanNo
From CHE
Left Join RecordingInfo as Rec
On CHE.LoanNumber = Rec.LoanNo
Where Channel = 'LINX'
and CHE.Doc in ('MTG','MOD')
and Rec.LoanNo is null
and LoanNumber >= '#LoanNo'
) A
order by LoanNo #Order
) B
order by LoanNo
I have not seen anyway to do with ties in linq.
I think something like this will work for you.
public static IQueryable<T> TopPercentWithTies<T, TKey>(this IOrderedQueryable<T> query, Expression<Func<T, TKey>> groupByExpression, double percent)
{
var groupedQuery = query.GroupBy(groupByExpression);
int numberToTake = groupedQuery.Count() * percent / 100;
return groupedQuery.Take(numberToTake).SelectMany(t => t);
}
I only tested it with IEnumerable, so I don't know for sure that it'll work properly with IQueryable. I also sorted the list before calling TopPercentWithTies().
Here's the code I used to test it.
int percent = 50;
var people = new []
{
new { Age = 99, Name = "Adam" },
new { Age = 99, Name = "Andrew" },
new { Age = 89, Name = "Bob" },
new { Age = 50, Name = "Cecil" },
new { Age = 50, Name = "Doug" },
new { Age = 50, Name = "Everett" },
new { Age = 35, Name = "Frank" },
new { Age = 25, Name = "Greg" },
new { Age = 15, Name = "Hank" }
};
var sortedPeople = people.AsQueryable().OrderByDescending(person => person.Age);
var results = sortedPeople.TopPercentWithTies(person => person.Age, percent);
foreach (var person in results)
Console.WriteLine(person);
Hope it helps or at least gets you in the right direction. You may want to tweak the logic for calculating numberToTake.